I've been working on this app for a while. I have several other modules that all work fine. I've been having a ton of trouble with this particular module and it's super frustrating. This problem looks super simple. Maybe I'm over thinking it. Hopefully someone will say that I am. :)
In this module, I decided to use methods from my model. This particular one is non-instanced. Here is my model:
/*
* Account.js
*/
module.exports = {
connection: 'islMongo',
attributes: {
name: {
type: 'string',
required: true,
},
},
numberToName: function(accountNumber) {
Account.findOne(accountNumber).exec(function(err, a){
if (err) {
return 'err';
} else {
return 'ok';
}
});
return 'broke';
},
};
I call it from one of my controllers like this:
var accountName = Account.numberToName(params.id);
At this point accountName's value is "broke". I don't understand why it wouldn't either return "err" or "ok". I simplified my actual function here for testing.
Edit:
I have other calls that work properly. For instance:
updateBalance: function(account, amount, callback) {
/* Accepts account id or account object */
(function _lookupAccount(afterLookup) {
if (typeof account === 'object') return afterLookup(null, account);
Account.findOne(account)
.exec(afterLookup);
})(function (err, a) {
if (err) return callback(err);
if (!a) {
err = new Error();
err.message = "Couldn't find account.";
err.status = 400;
return callback(err);
}
a.balance = parseInt(a.balance) + parseInt(amount);
a.save(callback);
});
},
Is called like this:
Account.updateBalance(params.account, -2000);
The definition has a callback, but I don't actually use one because it isn't needed. The method works fine.
Sails.js documentation provides example methods that don't use callbacks. They simply return the requested data.
// Attribute methods
getFullName: function (){
return this.firstName + ' ' + this.lastName;
},
isMarried: function () {
return !!this.spouse;
},
isEligibleForSocialSecurity: function (){
return this.age >= 65;
},
encryptPassword: function () {
}
And called like this:
if ( rick.isMarried() ) {
// ...
}
Which is what I am trying to do with my method at the top of this post. It seems like the exec() portion of Account.findOne() isn't even being called.
Sails.js & Node.js are asynchronous. So in simple words they don't wait for response from database, but when they got date they call a callback. So you need to read about Queries and callbacks and what is callback hell (you should never do that).
And now get back to your problem.
/*
Account.js
*/
//...
numberToName: function(accountNumber, callback) {
// if you want some additional logic you can create function here and call callback in it
Account.findOne(accountNumber).exec(callback);
}
//...
Tip: callbacks first param is always error.
// AccountController
method: function(req, res){
var id = req.param('id'); // if its int you should parseInt()
var callback = function(error, account){
if(error)
res.send('error');
else
res.send(account.name);
};
Account.numberToName(id, callback);
}
Related
I am trying to integrate google-recaptcha but no success.
Getting error
feedback.js:39 Uncaught TypeError: grecaptcha.render is not a function
main.js
'googlerecaptcha':'https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit',
define(['ojs/ojcore', 'knockout', 'jquery', 'appController', 'ckeditor', 'googlerecaptcha', 'ojs/ojlabel',
'ojs/ojknockout', 'ojs/ojinputtext', 'ojs/ojformlayout'],
function (oj, ko, $, app, ckeditor, grecaptcha) {
/**
* The view model for the main content view template
*/
function feedbackViewModel() {
var self = this;
// For small screens: labels on top
// For medium screens and up: labels inline
this.labelEdge = ko.computed(function () {
return app.smScreen ? "top" : "start";
}, this);
onloadCallback = function (a) {
grecaptcha.render('submit', {
'sitekey': 'YOUR_API_KEY',
'callback': self.onSubmit
}, true);
};
this.handleActivated = function (info) {
};
self.onSubmit = function (token) {
console.info("google recatpcha onSubmit", token)
//do validation/application code using token
var data = {secret: grecaptcha, response: recaptchaToken};
$.post({
url: "https://www.google.com/recaptcha/api/siteverify",
form: data
}).then(function (e) {
//recaptcha service called...check result
var resp = JSON.parse(e);
if (resp.success == false) {
console.info("recaptcha token outcome is false")
} else {
console.info("recaptcha token validated")
}
});
};
}
return feedbackViewModel;
});
Do you have a mapping for 'googlerecaptcha' in src/js/path_mapping.json? If I go to https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit, I do not see that it is returning any valid object. So most likely 'grecaptcha' variable is undefined.
reCaptcha + RequireJS
Looks like reCaptcha is a function that has to be executed vs an object that can be interacted with directly. So you may need a different approach, something mentioned in this thread.
I have been trying to do a specific operation once I receive the submitAdapterAuthentication from the challenge handler and I could not do any operation because my code it does not even compile through it. I am using the submitAdapterAuthentication in one method of my angular service. The method looks like this:
login: function (user, pass) {
//promise
var deferred = $q.defer();
//tempuser
tempUser = {username: user, password: pass};
userObj.user = user;
checkOnline().then(function (onl) {
if (onl) { //online
console.log("attempting online login");
var auth = "Basic " + window.btoa(user + ":" + pass);
var invocationData = {
parameters: [auth, user],
adapter: "SingleStepAuthAdapter",
procedure: "submitLogin"
};
ch.submitAdapterAuthentication(invocationData, {
onFailure: function (error) {
console.log("ERROR ON FAIL: ", error);
},
onConnectionFailure: function (error) {
console.log("BAD CONNECTION - OMAR", error);
},
timeout: 10000,
fromChallengeRequest: true,
onSuccess: function () {
console.log("-> submitAdapterAuthentication onSuccess!");
//update user info, as somehow isUserAuthenticated return false without it
WL.Client.updateUserInfo({
onSuccess: function () {
//return promise
deferred.resolve(true);
}
});
}
});
} else { //offline
console.log("attempting offline login");
deferred.resolve(offlineLogin());
}
uiService.hideBusyIndicator();
});
uiService.hideBusyIndicator();
return deferred.promise;
}
where ch is
var ch = WL.Client.createChallengeHandler(securityTest);
and checkOnline is this function that checks whether the user is online or not:
function checkOnline() {
var deferred = $q.defer();
WL.Client.connect({
onSuccess: function () {
console.log("** User is online!");
deferred.resolve(true);
},
onFailure: function () {
console.log("** User is offline!");
deferred.resolve(false);
},
timeout: 1000
});
return deferred.promise;
}
Finally this is the "submitLogin" procedure that I have in my SingleStepAuthAdapter.js. SingleStepAuthAdapter is the name of the adapter.
//-- exposed methods --//
function submitLogin(auth, username){
WL.Server.setActiveUser("SingleStepAuthAdapter", null);
var input = {
method : 'get',
headers: {Authorization: auth},
path : "/",
returnedContentType : 'plain'
};
var response = "No response";
response = WL.Server.invokeHttp(input);
WL.Logger.info('Response: ' + response.isSuccessful);
WL.Logger.info('response.responseHeader: ' + response.responseHeader);
WL.Logger.info('response.statusCode: ' + response.statusCode);
if (response.isSuccessful === true && (response.statusCode === 200)){
var userIdentity = {
userId: username,
displayName: username,
attributes: {
foo: "bar"
}
};
WL.Server.setActiveUser("SingleStepAuthAdapter", userIdentity);
return {
authRequired: false
};
}
WL.Logger.error('Auth unsuccessful');
return onAuthRequired(null, "Invalid login credentials");
}
So I am trying to send a promise to my controller in order to redirect the user to another page but the promise is not being returned as the challenge handler is not even working.
And by the way, I have followed this tutorial: https://medium.com/#papasimons/worklight-authentication-done-right-with-angularjs-768aa933329c
Does anyone know what this is happening?
Your understanding of the Challenge Handler and mine are considerably different.
Although the
ch.submitAdapterAuthentication()
is similar in structure to the standard adapter invocation methods I have never used any callbacks with it.
I work from the IBM AdapteBasedAuthentication tutorial materials
The basic idea is that your challenge handler should have two callback methods:
isCustomResponse()
handleChallenge()
You will see these functions invoked in response to your submission.
I suggest that start by looking at those methods. I can't comment on the ionic example you reference, but I have myself used angular/ionic with the authentication framework and challenge handlers. My starting point was the IBM material I reference above.
I am trying to create a simple Cloud Job on parse.com but it doesn't behave as expected.
The job returns without error but in the process I am making a find query that seems to be thrown out to the void. There is no error, my console.log are visible before executing query.find() but after that nothing... The query seems to fail silently.
Here is my code:
Parse.Cloud.job("maintenanceJob", function(request, status) {
return performMaintenanceTasks().then(function() {
status.success("Parse Job done");
}, function(errors) {
status.error(tools.prettifyError(errors));
});
});
function performMaintenanceTasks ()
{
// If we have more than NB_MAX_ITEMS objects in Items, let's delete some
var query = new Parse.Query(Items);
return query.count({
success: function(count) {
if(count > NB_MAX_ITEMS) {
return deleteOldItems(1); // 1 is used for test
}
return Parse.Promise.as("Nothing to do.");
},
error: function(error) {
return Parse.Promise.error(error);
}
});
}
function deleteOldItems(nbToDelete) {
// (...)
var query = new Parse.Query(Items);
query.ascending("createdAt");
query.limit(nbToDelete);
query.include("rawData");
console.log("I am visible in console, but NOTHING AFTER ME. query.find() seems to return immediately");
return query.find({
success: function (results) {
// I never pass here
var promise = Parse.Promise.as();
_.each(results, function (item) {
// For each item, extend the promise with a function to delete it.
promise = promise.then(function () {
var rawData = item.get("rawData");
// If we have a rawData, delete it before Item
if (rawData && rawData.id) {
return rawData.destroy({
success: function (theObj) {
return item.destroy({
success: function (anotherObj) {
// I never pass here
return Parse.Promise.as();
},
error: function (anotherObj, error) {
// I never pass here
return Parse.Promise.as();
}
});
},
error: function (theObj, error) {
// I never pass here
return Parse.Promise.as();
}
});
} else {
return item.destroy({
success: function (anotherObj) {
// I never pass here
return Parse.Promise.as();
},
error: function (anotherObj, error) {
// I never pass here
return Parse.Promise.as();
}
});
}
});
});
return promise;
},
error: function (error) {
// I never pass here
return Parse.Promise.error(error);
}
}).then(function (nil) {
// I never pass here
return Parse.Promise.as("DELETEOLDITEMS: Job finished");
}, function(error) {
// I never pass here
return Parse.Promise.error(error);
});
}
(I have tested to replace every // I never pass here with console.log(), without any result)
I tried many different things but I believe this should work! Or at least return errors!
Anyone know what I am doing wrong? Thanks in advance!
EDIT:
Even weirder, if I modify performMaintenanceTasks to skip query.count():
function performMaintenanceTasks()
{
return deleteOldItems(1);
}
the query.find() in deleteOldItems() is correctly executed this time!
What does that mean? Am I not allowed to nest queries on the same class?
I'm not certain if this pertains to you, but I know from my a personal experience that the Parse log can seem a little unintuitive. The Parse log only spits out 10 lines by default, so ensure you're specifying the log length every time you check.
parse log -n 1000
...is what I tend to do every time. This just makes debugging easier.
I use sails.js to update stock data from difference variable. When I do, console.log(product.stock), the value is 4. But it seems product.save() function below is not executed because attribute stock still not changed to 4. I guess the problem is at promise. Anybody know how to fix this?
exports.updateProductStock = function (details) {
details.forEach(function( detail ) {
var findPrevDetailRule = {
invoice: detail.invoice,
product: detail.product.id
};
var createPrevDetailRule = {
invoice: detail.invoice,
product: detail.product.id,
quantity: detail.quantity
};
var requirementBeforeUpdateStock = [
PreviousDetail.findOne(findPrevDetailRule).sort('createdAt desc').then(),
PreviousDetail.create(createPrevDetailRule).then()
];
Q.all(requirementBeforeUpdateStock)
.spread(function( prevDetail, newPrevDetail ) {
var difference = detail.quantity - prevDetail.quantity;
return {
prevDetail: prevDetail,
difference: difference
};
})
.then(function( results ) {
Product.findOne(results.prevDetail.product).then(function(product) {
product.stock += results.difference;
// maybe this below is not execute
product.save();
console.log(product.stock);
});
})
});
Note: I use sails 0.10-rc8. Have tested with sails-mysql, sails-mongo, sails-postgres, but still same.
.save() accepts a callback with which you can report success/failure.
For example, you can repost back to the client (from the sails documentation) :
product.save(function (err) {
if (err) return res.send(err, 500);
res.json(product);
});
Depending on scope, you may need to pass res into your function
Alternatively, monitor success/failure in the console, eg ,
product.save(function (err) {
if (err) console.log('save error: ' + err);//assuming err to be a string
console.log('save success');
});
Should at least give you a clue as to what's going wrong.
I've seen the myriad threads sprawled across the Internet about the following similar code in an AJAX request returning undefined:
AJAX.onreadystatechange = function() {
if(AJAX.readyState == 4) {
if(AJAX.status == 200) {
var response = AJAX.responseText;
return response;
}
else {
window.alert('Error: ' + AJAX.status);
return false;
}
}
};
I know that I'm supposed to "do something with" responseText like writing it to the HTML. The problem: I don't have that luxury. This bit of code is intended to be inside of a generic method for running fast AJAX requests that way all the code for making an AJAX request doesn't have to written out over and over again (~40×) with the chance of a minor problem here or there that breaks the application.
My method HAS to explicitly return responseText "or else." No writing to HTML. How would I do this? Also, I'd appreciate a lack of plugs for JQuery.
What I'm looking for:
function doAjax(param) {
// set up XmlHttpRequest
AJAX.onreadystatechange = function() {
if(AJAX.readyState == 4) {
if(AJAX.status == 200) {
var response = AJAX.responseText;
return response;
}
else {
window.alert('Error: ' + AJAX.status);
return false;
}
}
};
// send data
}
...
function doSpecificAjax() {
var param = array();
var result = doAjax(param);
// manipulate result
}
Doing a little research I came across this SOF post:
Ajax responseText comes back as undefined
Based on that post, it looks like you may want to implement your ajax method like this:
function doRequest(url, callback) {
var xmlhttp = ....; // create a new request here
xmlhttp.open("GET", url, true); // for async
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4) {
if (xmlhttp.status == 200) {
// pass the response to the callback function
callback(null, xmlhttp.responseText);
} else {
// pass the error to the callback function
callback(xmlhttp.statusText);
}
}
}
xmlhttp.send(null);
}
Then, you can call that method like this...
doRequest('http://mysite.com/foo', function(err, response) { // pass an anonymous function
if (err) {
return "";
} else {
return response;
}
});
This should return the responseText accurately. Let me know if this doesn't give you back the correct results.