Orchestrate has a client for NodeJS. It is based on promises so I should define a function then and a function fail. The first should be triggered when the request is successful and the second one when it fails. But sometimes it triggers both and I don't understand why.
Here is an example of a function that calls both then and fail:
user.checkToken = function (token, db, callback) {
if (token) {
db.get('acces_tokens', token).then(function (res) {
if (new Date(res.body.expire) > new Date()) {
//Token is valid
callback(res.body.user, res.body.expire);
} else {
//Token has expired
callback(false, "Token has expired");
}
}).fail(function (err) {
callback(false, "ERROR");
});
} else {
callback(false, "A token is needed");
}
};
I do not understand it :(
EDIT: It turn out it only does this when I call it from certain functions. This is even weirder.
EDIT 2: I've been doing some debuging. I console.logged the entire promise like this:
console.log(
db.get('acces_tokens', token)
.then(function (res) {
if (new Date(res.body.expire) > new Date()) {
//Token in valid
callback(res.body.user, res.body.expire);
} else {
//Token has expired
callback(false, "Token has expired");
}
}).fail(function (err) {
callback(false, "ERROR");
})
)
And I got this:
{ promise: [Circular],
_isPromise: true,
_successFn: null,
_failFn: [Function],
_scope: [Circular],
_boundArgs: null,
_hasContext: false,
_nextContext: undefined,
_currentContext: undefined }
As you can see _successFn is null. I guess this is the problem but I do not understand why it is null :(
But sometimes it triggers both and I don't understand why.
Because in the pattern you used - shortened to promise.then(callback).fail(callback) - callback might be called twice. If promise succeeds, your first callback is called, but when that callback throws an exception then the promise returned by promise.then(callback) is rejected and will call the fail callback you attached to it.
You'll need to use something along the lines of promise.then(callback, callback) to prevent this from happening. See When is .then(success, fail) considered an antipattern for promises? for more explanations.
Of course, it would be better if you'd screw callbacks alltogether and just return a promise from your checkToken function :-)
Related
This is my code in Javascript:
var params = {
FunctionName: "theTable",
InvokeArgs: JSON.stringify({ "name": "KirklandWA" })
};
lambda.invokeAsync(params, function (err, data) {
if (err) console.log(err, err.stack); // an error occurred
else {
console.log(data);
}
});
This is in Lambda:
exports.handler = async (event, context) => {
return "theReturnedValue";
};
What is happening is that it is not returning the theReturnedValue, instead returns
{Status: 202}
Status: 202
The code in Lambda is getting invoked, I made sure of it at Cloudwatch.
You're invoking with invokeAsync which will only return status code as stated in the documentation. Use invoke with InvocationType: "RequestResponse" instead
Reference:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#invoke-property
var lambda = new AWS.Lambda({});
var params = {
FunctionName: "function_name",
InvocationType: "RequestResponse"
};
response = lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
Verify that your lambda this selected as NodeJs 8.10 runtime, once having verified you must return a promise directly:
exports.handler = async (event, context) => {
return await new Promise((resolve, reject) => {
resolve({ message: "hello world" });
});
};
I was looking invokeAsync method definition in the aws-sdk documentation and the status code is fine:
Callback parameters:
err (Error) — the error object returned from the request. Set to null if the request is successful.
data (Object) — the de-serialized data returned from the request. Set to null if a request error occurs. The data object has the
following properties:
Status — (Integer) It will be 202 upon success.
I prepare you a basic example, please review handler.js file:
https://github.com/ns4lin4s/stackoverflow
Dont forget, that you must specify response body:
So when you finish, let me know how that works!
The problem is that your lambda function is not returning anything back to the caller.
Your handle funtion has a third parameter which is a callback function that is used to return results to the caller.
The callback function takes in two values, an Error and results
callback(Error error, Object result);
If you provide the Error values the lambda will throw the an error you provide to the user, if you dont provide an Error value but the results, its the results that will be returned
Its all documented very well here
Here are basic examples
callback(); // Indicates success but no information returned to the caller.
callback(null); // Indicates success but no information returned to the caller.
callback(null, "success"); // Indicates success with information returned to the caller.
callback(error); // Indicates error with error information returned to the caller.
Your handler function should be.
exports.handler = async (event, context,callback) => {
callback(null, "theReturnedValue");
};
In the following function, it is my understanding that the code should stop executing after the callback if either of the first two if conditions is met, i.e. if either "validate_post" returns an error or if "!clean.postId == true". When I ran into the second if condition during testing, the callback was called with the error message, however the following block (data.updatePost()) was still executed, resulting in the callback being called again. What am I doing wrong here? Thanks for your help!
var data = require('./data')
var validate_post = require('./validate_post')
module.exports = function(post, callback) {
validate_post(post, function(err, clean) {
if(err) {
callback({status: "error", message: err})
return
}
if(!clean.postId) {
console.log('first callback is called at this point')
callback({status: "error", message: "Something is off here"})
return
}
console.log('code still executes for some reason')
data.updatePost(clean.postId, clean.post, function(err, res) {
if (err) {
callback({status: "error", message: err})
return
}
console.log('callback is called a second time at this point')
callback(null, {status: "success", message: res})
})
})
}
It turns out I forgot to put a return statement in the validate_post()function resulting in the callback from validate_post() being fired twice. My problem is resolved now after fixing the validate_post() function. Thanks!
I am writing an updated testing library for Node.js and am trying to properly trap errors that occur in test callbacks
for some reason, the following code doesn't trap an AssertionError:
process.on('uncaughtException',function(err){
console.error(err); //an instance of AssertionError will show up here
});
[file1,file2,file2].forEach(function (file) {
self.it('[test] ' + path.basename(file), {
parallel:true
},function testCallback(done) {
var jsonDataForEnrichment = require(file);
request({
url: serverEndpoint,
json: true,
body: jsonDataForEnrichment,
method: 'POST'
}, function (error, response, body) {
if (error) {
done(error);
}
else {
assert(response.statusCode == 201, "Error: Response Code"); //this throws an error, which is OK of course
done();
}
});
});
});
I handle the callback (I named it "testCallback" above), with this code:
try {
if (!inDebugMode) {
var err = new Error('timed out - ' + test.cb);
var timer = setTimeout(function () {
test.timedOut = true;
cb(err);
}, 5000);
}
test.cb.apply({
data: test.data,
desc: test.desc,
testId: test.testId
}, [function (err) { //this anonymous function is passed as the done functon
cb(err);
}]);
}
catch (err) { //assertion error is not caught here
console.log(err.stack);
cb(err);
}
I assume the problem is that callbacks that result from async functions like those made in the request module, cannot be trapped by simple error handling.
What is the best way to trap that error?
Should I just flesh out the process.on('uncaughtException') handler? Or is there a better way?
The best way to handle this appears to be Node.js domains, a core module
https://nodejs.org/api/domain.html
it will likely be deprecated soon, but hopefully there will be a replacement that can have similar functionality, because the domain module is saving my ass right now, as I have no other way to trap errors, because the errors might be generated by my users' code, not my code.
I am trying to throw a user defined error while updating a documents in mongoDb, if any error occures in Method. I am calling method and trying to catch the error, but i am not getting one. Error is printed only in server console. how can i catch the error in client side?
My code sample seems like this:
//Method
methodName: (userData) ->
if(Meteor.isServer and this.userId)
User.update {_id:this.userId},{$set:{username:userData.username, "profile.name": userData.name ,"emails.$.address": userData.email}}, (error) ->
if error and error.code == 11000
throw new Meteor.Error 403, 'Email already in used'
//client Side
$meteor.call('methodName',result).then ((success) ->
console.log success // is undefined in both case, when updates and in case of error
if success
console.log 'data updated'
), (err) ->
console.log err // not entered in this region
Your code has a large amount of errors.
Meteor.methods({
methodName: function(userData){
// you have to create the $set hash programatically first
var setHash = {
"$set": {
"username": userData.username,
"profile.name": userData.name,
// what's going on here with the dollar sign you originally had?
"emails.0.address": userData.email
}
};
if( Meteor.isServer && this.userId() ){
// It's Users, not User
return Users.update( { _id: this.userId() }, setHash, function(error, numberOfAffectedDocuments){
if(error && error.code == "11000"){
// Error names have to be a string
throw new Meteor.error("403", "Email already in use.");
} else {
return "Success! The number of affected documents is " + numberOfAffectedDocuments;
});
};
}
});
// in your original code you never sent your Meteor Method any arguments when calling it
Meteor.call('methodName', userDataObject, function(error, result){
if(error){
console.log("there was an error: ", error);
} else {
console.log("method call was a success: ", result);
};
});
References:
http://docs.mongodb.org/manual/reference/operator/update/set/#set-elements-in-arrays
http://docs.meteor.com/#/full/meteor_user
Your code has a couple of misconceptions
1) A method is a synchronous call. This means if it returns or runs completely before an error is fired the error callback will not be called on the client.
This means you need to use synchronous code throughout. At the moment, you are using a callback.
You can use this approach instead:
Meteor.methods methodName: (userData) ->
if Meteor.isServer and #userId
return User.update({ _id: #userId }, $set:
username: userData.username
'profile.name': userData.name
'emails.$.address': userData.email)
This will throw an error that will be received on the client with reason "Internal Server Error". As a 'catch-all' error. The difference between your code is there is no callback.
You can catch your specific duplicate key error using the try..catch pattern using this syntax.
2) User.update {_id:this.userId} will always run. You are looking for a "duplicate key", 11000, error when you update the document. This isn't the best approach to doing this. You should directly check for the email beforehand.
3) A method should return a value. At the moment, you don't return anything. You can only use one for the result, a callback or checking the value given back by the method. At the moment, you do both, so the result of User.update is undefined. This is why you see undefined. This should work:
Meteor.methods methodName: (userData) ->
if Meteor.isServer and #userId
if(User.emails.address':userData.email})) throw new Meteor.Error(500, "Email already exists");
return User.update({ _id: #userId }, $set:
username: userData.username
'profile.name': userData.name
'emails.$.address': userData.email)
else return false
So here you would directly check for a user having used the email and throw the Error & update it if it's not used. There are no callbacks so it should return a value to Meteor.call on the client.
I'm new to Promises in JavaScript, and whilst it seems to be working for me to an extent, I'm unable to test the 'reject' value.
I'm passing through an Error, and want to ensure that it is an error and more importantly, that the error code matches what I'm expecting.
return new Promise(function(resolve, reject){
tableService.deleteEntity(config.azureTable.tableName,
visitor.azureEntity(), function (error, response) {
// If successful, go on.
if (!error) {
resolve(response);
}
// If unsuccessful, log error.
else {
/* If we know it's a resourceNotFound
that's causing the error, return that. */
if (error.code === 'ResourceNotFound') {
reject(new Error('Record not found'));
}
// For unexpected errros.
else {
reject(new Error('Table service error (delete): ' + error));
}
}
});
});
The test, in Mocha - using chai and chai-as-promised. Everything else is working (I have 24 passing tests) - but this one has me stuck!
it('return an error when the lookup fails', function (done) {
storage.delete(globalUUID).then(function(sucess) {
done(sucess);
}, function(error) {
expect(error).to.be.an(Error);
done();
});
});
Any help would be greatly appreciated.
You are not using chai-as-promised anywhere. If your first code example is the body of the storage.delete method, then your test should look like:
it('return an error when the lookup fails', function() {
expect(storage.delete(globalUUID)).to.be.rejectedWith(Error);
});