I'm trying to create an Azure Function that will take take in parameter(s) and return a value stored in an Azure Table. I believe the issue I'm having has more to do with javascript than it does with the Azure Table SDK.
How are you supposed to return the value from the query via http response? I have attached a copy of the code and it should explain where I'm confused. My main confusion is due to the fact that I'm able to call context.log() but unable to call context.res{} from the function in the query method.
I know that scope has something to do with it but I am not an expert when it comes to javascript and nested functions. Some guidance or example would be appreciated
var azure = require('azure-storage');
module.exports = function (context, req) {
context.log('Some Function');
var hostUri = 'https://*******.table.core.windows.net'
var sasToken = 'abc123'
if (req.query.value) {
var tableService = azure.createTableServiceWithSas(hostUri, sasToken)
var nothing = tableService.retrieveEntity('Table', 'Partition', 'Row', function(error, result, response) {
if (!error) {
context.log('I am able to send data to the logs here')
context.res = {
status: 200,
body: "This is what I am tring to return -> " + JSON.stringify(result)
};
}
})
context.res = {
status: 200,
body: "I'm able to get a response here"
};
}
else {
context.res = {
status: 400,
body: "Somthing went wrong..."
};
}
context.done();
};
Solution first:
if (req.query.value) {
var tableService = azure.createTableServiceWithSas(hostUri, sasToken)
tableService.retrieveEntity('Table', 'Partition', 'Row', function(error, result, response) {
if (!error) {
context.log('I am able to send data to the logs here')
context.res = {
status: 200,
body: "This is what I am tring to return -> " + JSON.stringify(result)
};
}
else{
context.res = {
status: 400,
body: error
};
}
context.done();
})
}
else {
context.res = {
status: 400,
body: "Value is empty"
};
context.done();
}
Explanation:
Let's mark the context.res in order as c1 and c2 to avoid redundant.
You are caught by callback function. The callback function(error, result, response){} is not executed until entity is retrieved from remote sever(or error happens). This operation may take some time, the program continues executing while waiting for it to complete, this pattern is called asynchronous.
So your code snippet c1 in callback is not executed immediately but instead c2 is. Then context.done(); runs before the entity retrieved(you see response message I'm able to get a response here), callback is hence never called.
Code relying on result of callback should be included in callback, so that it can execute exactly after callback is finished. Put context.done(); in both callback and else segment to assure it won't run in advance.
Also one blog about asynchronous-javascript for you to refer.
Related
I have this function for testing sending notification to all users.
export const sendTestingNotification = functions.https.onRequest((request, response) => {
const message = "Hello world";
const body = "This is body"
const getAllUsersPromise = admin.database().ref('/users').once('value')
const payload = {
notification: {
title: message,
body: body
}
}
return getAllUsersPromise.then(results => {
var tokens : string[] = [];
console.log("Child Snapshot count: ", results.numChildren())
results.forEach(childSnapshot => {
var childData = childSnapshot.val();
var instanceId = String(childData.instanceId)
if (childData.instanceId != null) { tokens.push(instanceId); }
})
console.log('final tokens = ',tokens," notification= ",payload);
return admin.messaging().sendToDevice(tokens, payload).then(response2 => {
console.log ("Done sending notification call. Entering callback.")
const tokensToRemove : string[] = [];
response2.results.forEach ((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to instance id = ', tokens[index], error);
}
else {
console.log("Successfully send notification to ", tokens[index])
}
});
return Promise.all(tokensToRemove);
})
.catch(console.log.bind(console));
})
.catch(console.log.bind(console));
});
I have only one user in firebase database, and that one user has instanceId.
Here's the console log:
Child Snapshot count: 1
final tokens = [ 'eMZHr5WgHmU:APA91bEKg8wAS5qYMxuSJqn...
Done sending notification call. Entering callback.
Successfully send notification to eMZHr5WgHmU:APA91bEKg8wAS5qYMxuSJqn...
Function execution took 60002 ms, finished with status: 'timeout'
What's left of my function execution so that it finishes properly? The browser that call the function never stops show loading indicator.
If I add a response.send before return Promise, the browser loading finished. But checking at the log showed me that the process still working and returned the "Function execution took 60002 ms, finished with status: 'timeout'" error. How can I fix this?
With HTTP type functions, you need to send a response to the caller to terminate the function. For all code paths in your function, you should be calling response.send() or something that will send that response. Please consult the documentation for more information an examples. In particular, read the section on terminating a function.
I have a function that posts data to the database. It works fine but I would also like to use the same function to send a message to trigger another function.
I have tried to simultaneouslysend the message and make the post request but at this moment only the post request works
Here is what my code looks like
const params = {
"TableName": "sites",
"Item": {
userId: event.requestContext.identity.cognitoIdentityId,
siteId: siteIdFinal,
...data,
createdAt: Date.now()
}
};
const messageParams = {
MessageBody: 'Waddup fam',
QueueUrl: ' https://sqs.eu-west-1.amazonaws.com/106845550704/MyQueue'
};
try {
await dynamoDbLib.call("put", params);
sqs.sendMessage(messageParams, (err, data) => {
if (err) {
console.log("Error: "+err);
} else {
console.log("Success: "+data.MessageId);
}
});
return success(params.Item);
} catch (e) {
console.log(e);
return failure({ status: false });
}
I am not getting any error I am just getting returned back the data that has been posted. I thought i should receive the message Id of the message I sent but I am not getting that. When ever I look at the cloudwatch logs, the message isnt sent
Your async function returns params.Item before sendMessage executes a callback.
Use a promise to make sure that both methods finish properly
await sqs.sendMessage(messageParams).promise()
.then(function(data) {
console.log("Success: "+data.MessageId);
}).catch(function(err) {
console.log("Error: "+err);
});
More on aws-sdk and promises:
https://aws.amazon.com/blogs/developer/support-for-promises-in-the-sdk/
I'm having trouble getting this to run. It runs just fine if I make the same FB.api call directly from the front end or through "serverless invoke local" and it console.logs my response. But when I deploy this function to a lambda and try to call it, I hit the "console.log("TRY"), get a 502 error, and then nothing after that. No response from the FB.api call, no errors, no info at all. I've tried upping the timeout as well and have brought it up to as much as 15 seconds and still getting no response. Anyone else run into this? Thanks!
export async function main(event, context, callback){
var FB = require('fb');
const data = JSON.parse(event.body)
console.log("DATA: ", data)
const requestString = data.Id + '/accounts'
console.log(requestString)
console.log("ACCESS TOKEN: ", data.accessToken)
const pages = []
try{
console.log("TRY")
await FB.api(requestString, 'get', { access_token: data.accessToken }, function(response){
console.log("RESPONSE: ", response)
callback(null, success(response));
})
}
catch (e){
console.log("CATCH")
console.log(e)
callback(null, failure({ status: false }));
}
}
So I'm having trouble getting one javascript function to finish before the next one starting. I've spent quite a lot of time trying to use callback methods described on other stackoverflow posts. I could get simple examples that used timeouts to work but couldn't get it to work with my API request. I stumbled upon async.js and thought that perhaps using async.series would be a good idea to get my two functions to perform one after another. So I tried this approach, however I still seem to be having the problem where the first function takes a bit longer to execute (which is fine) but the execution process moves past this function instead of waiting for it to end. I feel I have a misconception of some sort since I have tried several methods but to no avail.
What is strange is, is that that when running server.js, it goes into the first function but then it leaves the async.series() function even before the request is finished. When I print inside of tokenReq(), I can see that the request was successful as a token code is returned successfully however this happens to late as execution has moved on. The output is shown below.
server.js:
var access_code;
async.series([
function() {
access_code = queries.data.tokenReq(code);
console.log("Finished inside function 1");
},
function() {
console.log("\n Starting function 2 \n");
if (access_code === "error") {
res.json("An error has occured");
} else {
var response = queries.data.messagesReq(access_code);
res.json(response);
}
}
],
function(err, access_code) {
});
console.log("Outside");
queries.js:
tokenReq: function(code) {
var tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
var form = {
code: code,
client_id: "__ID__",
redirect_uri: "__Site__/",
grant_type: "authorization_code",
client_secret: "__Secret__",
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: tokenUrl,
body: formData,
method: 'POST'
}, function (error, response, body) {
if (error != "null") {
var access_token = JSON.parse(body).access_token;
console.log("\n INSIDE FUNCTION REQUEST, Token: " + access_token + " \n");
return access_token;
} else {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
// console.log('body:', body); // Print the HTML for the Google homepage.
return "error";
}
});
},
Output:
Finished inside function 1
Outside
INSIDE FUNCTION REQUEST, Token: 8Swhd.......
You missed a major point here. Since node.js is asynchronous there should not be a way to know when a function completes its execution. That is why we specify callbacks so that the invoking function knows whom to call when it finishes its execution. Once you have functions with callbacks, you can enforce series/parallel/waterfall behavior with async module.
tokenReq: function(code, cb) {
var tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
var form = {
code: code,
client_id: "__ID__",
redirect_uri: "__Site__/",
grant_type: "authorization_code",
client_secret: "__Secret__",
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
request({
headers: {
'Content-Length': contentLength,
'Content-Type': 'application/x-www-form-urlencoded'
},
uri: tokenUrl,
body: formData,
method: 'POST'
}, function (error, response, body) {
if (error != "null") {
var access_token = JSON.parse(body).access_token;
console.log("\n INSIDE FUNCTION REQUEST, Token: " + access_token + " \n");
return cb(null, access_token);
} else {
console.log('error:', error); // Print the error if one occurred
console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received
// console.log('body:', body); // Print the HTML for the Google homepage.
return cb(new Error("whatever"));
}
});
},
Now, you can use the callback inside server.js
var access_code;
async.series([
function(cb) {
return queries.data.tokenReq(code, cb);
},
function(access_code, cb) {
console.log("\n Starting function 2 \n");
if (access_code === "error") {
res.json("An error has occured");
} else {
var response = queries.data.messagesReq(access_code);
res.json(response);
}
// do whatever you want after this
return cb();
}
],
function(err, access_code) {
if (err) {
console.log(err);
}
// wrap your logic around a function and call the correspoding callback here
});
I'm a bit new to all this (including Javascript callbacks and ES6). I'm using NodeJS + Express + MongoDB.
I'm calling an Ajax function to update an item and the success Ajax call is never done.
Here is my Ajax call (called from React)
editBug : function(bug){
console.log('about to edit bug with these values',bug);
$.ajax({
url:'/api/bugs',
method: 'PUT',
data:bug
})
.done((jqxhr) => {
console.log('succcess while editing the bug');
this.setState({successVisible : true});
})
.fail((jqxhr) => {
console.log('error : ' + jqxhr);
})
},
Here is my API function:
app.put('/api/bugs',function(req,res){
//console.log('req',req);
console.log('query string : ',req.query);
console.log('query params : ',req.params);
console.log('query body: ',req.body);
let id = new ObjectID(req.body._id);
req.body._id = new ObjectID(req.body._id);
db.collection('bugs').replaceOne(
{_id:id},
req.body,
function(err,result){
assert.equal(err,null);
console.log('Successfull replace!');
res.status(200);
}
);
});
The Successfull replace! log is correctly shown on the server side.
The about to edit bug with these values is correctly shown on the front side. But the succcess while editing the bug log is not shown on front end and it seems .done call is never executed.
The problem is that you are not sending any response back to the browser on node side. Try the following snippet and you should be good to go
Also, I'd like to point out that you should handle the errors. While updating the bugs if something goes wrong, the best practice would be to inform the browser with the 500 status code indicating that the intended action failed. I've added this aspect in the snipped below
app.put('/api/bugs', function(req, res) {
//console.log('req',req);
console.log('query string : ', req.query);
console.log('query params : ', req.params);
console.log('query body: ', req.body);
let id = new ObjectID(req.body._id);
req.body._id = new ObjectID(req.body._id);
db.collection('bugs').replaceOne({
_id: id
},
req.body,
function(err, result) {
if (err) {
console.log('Failed replace');
res.status(500).end(); // <- We set the response status code and end the request
} else {
assert.equal(err, null);
console.log('Successfull replace!');
res.status(200).end(); // <- We set the response status code and end the request
}
}
);
});
Don't you need to end your response object on the Node.js side?
Try adding res.end(); or any kind of response to your response object.
Also, you can use chrome's (or any other browser's) network tab to actually see how your AJAX requests end up, to see if they hang or finish.