getting Task timed out after 59.05 seconds aws lambda nodejs - node.js

I am trying to write a lambda function in node 8.10 supported by aws lambda
this is with reference to my previous question
I am trying node async/await , same code work when I run it in my local(and is not even taking a second to get the response back) but when I am trying it in lambda getting time out error, here the code control is getting inside promise and printing url urltohit with the correct url but after it is getting stuck, have tried different thing like increasing the time in lambda config and even tried node request instead of http but still getting the same error.
'use strict';
var http = require('http');
var request = require('request');
exports.handler = async (event) => {
const response = await getRequest(urltohit);
console.log(response);
}
const getRequest = async (url) => {
console.log("Test log");
return new Promise((resolve, reject) => {
console.log(`url ${url}`);
http.get(url, function(res) {
console.log("Got response: " + res.statusCode);
resolve(res.statusCode);
}).on('error', function(e) {
console.log("Got error: " + e.message);
reject(e.message);
});
});
}

You need to call callback after function execution.
exports.handler = async (event, context, callback) => {
const response = await getRequest(urltohit);
console.log(response);
callback(null);
}
It's lambda behaviour to keep function running until callback called.
More could be found in official documentation

Related

Make POST https request in AWS Lambda without waiting for response

I am trying to make an https request (API gateway call to other Lambda) in my AWS Lambda function with Node.js, but without waiting for the response, so when the data has already been sent to the other Lambda (and starts running), my first Lambda stops running.
What I've been trying to do is the following code:
await new Promise((resolve, reject) => {
const options = {
hostname: 'example.com',
path: '/example/action',
method: 'POST'
}
const req = https.request(options);
console.log('Request: ', req);
req.on('error', error => {
reject(error);
});
// Send data
req.write(JSON.stringify(data));
req.end(() => {
// This is where (in theory) request has been made, and no response received
resolve(req);
});
});
It results in a timeout issue, due to resolve is never called.
Any ideas?

Make HTTP GET from fulfillment in Node.JS

I'm trying to make my own google action and I want to call an external api to get responses.
Here is my code:
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
const app = conversation({debug:true});
const https = require('https');
app.handle('Tester', conv => {
// Implement your code here
conv.add("ok it works");
});
app.handle('Tester2', conv => {
// Implement your code here
let url = 'https://jsonplaceholder.typicode.com/users?_limit=2';
//const url = "https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22";
http_req(url).then((message)=> {
console.log(message[0].name);
conv.add(message[0].name);
//return Promise.resolve();
});
});
function http_req(url) {
return new Promise((resolve, reject) => {
https.get(url, function(resp) {
var json = "";
resp.on("data", function(chunk) {
//console.log("received JSON response: " + chunk);
json += chunk;
});
resp.on("end", function() {
let jsonData = JSON.parse(json);
console.log(jsonData[0].name);
resolve(jsonData);
});
}).on("error", (err) => {
reject("Error: " + err.message);
});
});
}
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
The logs:
Error text:
Error: Response has already been sent. Is this being used in an async call that was not returned as a promise to the intent handler?
The problem is that the assistant won't say the conv.add(message[0].name); (obviusly it has a value)
Thanks in advance!
Thanks to a reddit user
https://www.reddit.com/r/GoogleAssistantDev/comments/lia5r4/make_http_get_from_fulfillment_in_nodejs/gn23hi3?utm_source=share&utm_medium=web2x&context=3
This error messages tells you just about all you need to know! Your
call to con.add() is indeed being used in an asynchronous call (the
callback chained to the Promise you created from http_req), and you
are indeed not returning that Promise.
Here's what's happening:
Google calls your 'Tester2' handler
You start an asynchronous HTTP request via http_req, encapsulated in a
Promise
Your function completes before the HTTP request does
Google sees that you are not returning anything from your handler and
assumes that you're done, so it sends the Response
The HTTP request finishes and its Promise resolves, calling your code
attached by the then() function
The simple solution here is to return the Promise created by your
http_req(...).then(...) code, so Google will know that you're not just
quite done, and it should wait for that Promise to resolve before
sending the Response.
If you can use async/await it becomes a bit clearer:
app.handle('Tester2', async conv => {
// Implement your code here
let url = 'https://jsonplaceholder.typicode.com/users?_limit=2';
//const url = "https://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b6907d289e10d714a6e88b30761fae22";
const message = await http_req(url);
console.log(message[0].name);
conv.add(message[0].name);
});

AWS X Ray node js trails not showing

I am using Lambda (Node 8.10) and working with AWS X Ray. I am calling an external ip address using promise.
When I call, other traces are shown but cannot get custom segment.
I am not using any frameworks just a pure node js.
const AWSXRay = require('aws-xray-sdk-core');
AWSXRay.enableManualMode();
AWSXRay.captureHTTPsGlobal(require('https'));
const https = AWSXRay.captureHTTPs(require('https'));
exports.handler = async (event, context, callback) => {
// other code
const response = await doSomething(event);
return callback(error, response);
};
async doSomething(event) {
return new Promise((resolve, reject) => {
const segment = new AWSXRay.Segment('custom_segment_here');
AWSXRay.captureAsyncFunc('send', (subsegment) => {
const options = {
hostname: host,
port: 443,
path: '/',
method: 'GET',
XRaySegment: subsegment,
};
const req = https.request(options, (res) => {
code = res.statusCode;
resolve(code);
});
req.on('error', (error) => {
subsegment.addError(error);
reject(error);
});
subsegment.close();
req.end();
}, segment);
}
}
In Lambda scenario, Lambda is responsible for creating Segments and AWS X-Ray SDKs only create Subsegments and then emits them. Based on your code snippet, you created a segment (const segment = new AWSXRay.Segment('custom_segment_here');) inside a lambda function which couldn't be emitted so that you cannot see it in our console. Hope my answer is clear. :)

How do I call a third party Rest API from Firebase function for Actions on Google

I am trying to call a rest API from Firebase function which servers as a fulfillment for Actions on Google.
I tried the following approach:
const { dialogflow } = require('actions-on-google');
const functions = require('firebase-functions');
const http = require('https');
const host = 'wwws.example.com';
const app = dialogflow({debug: true});
app.intent('my_intent_1', (conv, {param1}) => {
// Call the rate API
callApi(param1).then((output) => {
console.log(output);
conv.close(`I found ${output.length} items!`);
}).catch(() => {
conv.close('Error occurred while trying to get vehicles. Please try again later.');
});
});
function callApi (param1) {
return new Promise((resolve, reject) => {
// Create the path for the HTTP request to get the vehicle
let path = '/api/' + encodeURIComponent(param1);
console.log('API Request: ' + host + path);
// Make the HTTP request to get the vehicle
http.get({host: host, path: path}, (res) => {
let body = ''; // var to store the response chunks
res.on('data', (d) => { body += d; }); // store each response chunk
res.on('end', () => {
// After all the data has been received parse the JSON for desired data
let response = JSON.parse(body);
let output = {};
//copy required response attributes to output here
console.log(response.length.toString());
resolve(output);
});
res.on('error', (error) => {
console.log(`Error calling the API: ${error}`)
reject();
});
}); //http.get
}); //promise
}
exports.myFunction = functions.https.onRequest(app);
This is almost working. API is called and I get the data back. The problem is that without async/await, the function does not wait for the "callApi" to complete, and I get an error from Actions on Google that there was no response. After the error, I can see the console.log outputs in the Firebase log, so everything is working, it is just out of sync.
I tried using async/await but got an error which I think is because Firebase uses old version of node.js which does not support async.
How can I get around this?
Your function callApi returns a promise, but you don't return a promise in your intent handler. You should make sure you add the return so that the handler knows to wait for the response.
app.intent('my_intent_1', (conv, {param1}) => {
// Call the rate API
return callApi(param1).then((output) => {
console.log(output);
conv.close(`I found ${output.length} items!`);
}).catch(() => {
conv.close('Error occurred while trying to get vehicles. Please try again later.');
});
});

Sinon NodeJS stub only for module under test

I have a module under test which uses https to PUT data to a response URL. Before doing so, it makes calls to the AWS SDK. I do not want to stub the calls that AWS SDK makes using https, but I do want to stub the call to https.post that my module under test uses (it's an AWS Lambda unit test if that matters).
Consider the following test code
describe('app', function () {
beforeEach(function () {
this.handler = require('../app').handler;
this.request = sinon.stub(https, 'request');
});
afterEach(function () {
https.request.restore();
});
describe('#handler()', function () {
it('should do something', function (done) {
var request = new PassThrough();
var write = sinon.spy(request, 'write');
this.request.returns(request);
var event = {...};
var context = {
done: function () {
assert(write.withArgs({...}).calledOnce);
done();
}
}
this.handler(event, context);
});
});
});
And my module under test (app.js)
var aws = require("aws-sdk");
var promise = require("promise");
exports.handler = function (event, context) {
var iam = new aws.IAM();
promise.denodeify(iam.getUser.bind(iam))().then(function (result) {
....
sendResponse(...);
}, function (err) {
...
});
};
// I only want to stub the use of https in THIS function, not the use of https by the AWS SDK itself
function sendResponse(event, context, responseStatus, responseData) {
var https = require("https");
var url = require("url");
var parsedUrl = url.parse(event.ResponseURL);
var options = {
...
};
var request = https.request(options, function (response) {
...
context.done();
});
request.on("error", function (error) {
...
context.done();
});
// write data to request body
request.write(...);
request.end();
}
How can I accomplish this?
You could use nock to mock specific HTTP/S requests, rather than function calls.
With nock, you can setup URL and request matchers that will allow requests through that don't match what you've defined.
Eg:
nock('https://www.something.com')
.post('/the-post-path-to-mock')
.reply(200, 'Mocked response!');
This would only intercept POST calls to https://www.something.com/the-post-path-to-mock, responding with a 200, and ignore other requests.
Nock also provides many options for mocking responses or accessing the original request data.

Resources