Make POST https request in AWS Lambda without waiting for response - node.js

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?

Related

Twilio: forward a call to a flow in Twilio

I want to fordward a call to a Studio Flow after the agent in flex hangs up so a CSAT survey can play for the user.
I created a plugin that calls a function inside Twilio but there is a "Error - 11200" after the forwarding is done.
I replaced the hang up action so it redirects the call to a function in twilio. The function is supossed to send the call to a flow that will play the survey. I suspect the problem has to do with authentication but I can't find much about it.
I'm fairly new to twilio, so any help will be greatly appreciated
This is the part of the plugin that calls the function:
flex.Actions.replaceAction("HangupCall", (payload) => {
console.log('task attributes: ' + JSON.stringify(payload.task.attributes));
if (payload.task.attributes.direction === "inbound") {
// Describe the body of your request
const body = {
callSid: payload.task.attributes.call_sid,
callerId: payload.task.attributes.from,
destination: '+18xxxxxxxx',
Token: manager.store.getState().flex.session.ssoTokenPayload.token
};
// Set up the HTTP options for your request
const options = {
method: 'POST',
body: new URLSearchParams(body),
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
};
// Make the network request using the Fetch API
fetch('https://TWILIO INSTANCE.twil.io/TransferUtil', options)
.then(resp => resp.json())
.then(data => console.log(data));
} else {
original(payload);
}
});
And this is the function in twilio:
const TokenValidator = require('twilio-flex-token-validator').functionValidator;
exports.handler = TokenValidator(async function(context, event, callback) {
const response = new Twilio.Response();
response.appendHeader('Access-Control-Allow-Origin', '*');
response.appendHeader('Access-Control-Allow-Methods', 'OPTIONS, POST, GET');
response.appendHeader('Access-Control-Allow-Headers', 'Content-Type');
response.appendHeader('Content-Type', 'application/json');
const client = require('twilio')();
const callSid = event.callSid;
const callerId = event.callerId;
const destination = event.destination;
console.log('Call Sid:', callSid);
console.log('Transfer call from:', callerId, 'to:', destination);
try {
let url = 'https://studio.twilio.com/v2/Flows/FWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Executions';
let call = await client.calls(callSid).update({method: 'POST', url: encodeURI(url)});
console.log(JSON.stringify(call));
response.appendHeader('Content-Type', 'application/json');
response.setBody(call);
callback(null, response);
}
catch (err) {
response.appendHeader('Content-Type', 'plain/text');
response.setBody(err.message);
console.log(err.message);
response.setStatusCode(500);
callback(null, response);
}
});
EDIT:
In the error Log I get this information:
Argh, I read the error wrong. There isn't anything wrong with the Function, the error is coming from the call trying to make a webhook request to the URL https://studio.twilio.com/v2/Flows/FWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/Executions. That's the REST API trigger and needs to be requested in the same way as any other API request using your account credentials or API keys.
You should set that URL to the webhook trigger URL, which looks like https://webhooks.twilio.com/v1/Accounts/${ACCOUNT_SID}/Flows/${FLOW_SID}. Then the call will be able to request it as part of a normal webhook flow.

Making an HTTPs request on AWS ApiGatewayV2 websocket connection to Respond, or Delete it

UPDATE
This issue is partially resolved, the problem now lies in authenticating the ApiGateway request. I am unsure of how to acquire the necessary tokens to send with the request so that it is valid, because this is a [serverless-framework] service so I can't use the AWS Console to copy paste the tokens into the request's json data. Moreover, I wouldn't know what json key they'd have to be under anyways. So I guess this question has changed considerably in scope.
I need to respond/delete an active websocket connection established through AWS ApiGatewayV2, in a Lambda. How do I use node js to send a POST request that ApiGateway can understand?
I saw on the websocket support announcement video that you could issue an HTTP POST request to respond to a websocket, and DELETE request to disconnect a websocket. Full table from the video transcribed here:
Connection URL
https://abcdef.execute-api.us-west-1.amazonaws.com/env/#connections/connectionId
Operation Action
POST Sends a message from the Server to connected WS Client
GET Gets the latest connection status of the connected WS Client
DELETE Disconnect the connected client from the WS connection
(this is not documented anywhere else, AFAIK)
Seeing as the AWS SDK does not provide a deleteConnection method on ApiGatewayManagementApi, I need to be able to issue requests directly to the ApiGateway anyways.
const connect = async (event, context) => {
const connection_id = event.requestContext.connectionId;
const host = event.requestContext.domainName;
const path = '/' + event.requestContext.stage + '/#connections/';
const json = JSON.stringify({data: "hello world!"});
console.log("send to " + host + path + connection_id + ":\n" + json);
await new Promise((resolve, reject) => {
const options = {
host: host,
port: '443',
path: path + connection_id,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(json)
}
};
const req = https.request(
options,
(res) => {
res.on('data', (data) => {
console.error(data.toString());
});
res.on('end', () => {
console.error("request finished");
resolve();
});
res.on('error', (error) => {
console.error(error, error.stack);
reject();
});
}
);
req.write(json);
req.end();
});
return success;
};
When I use wscat to test it out, this code results in the console.log showing up in CloudWatch:
send to ********.execute-api.us-east-2.amazonaws.com/dev/#connections/*************:
{
"data": "hello world!"
}
...
{
"message": "Missing Authentication Token"
}
...
request finished
And wscat says:
connected (press CTRL+C to quit)
>
But does not print hello world! or similar.
Edit
I was missing
res.on('data', (data) => {
console.error(data.toString());
});
in the response handler, which was breaking things. This still doesnt work, though.
You're likely missing two things here.
You need to make an IAM signed request to the API Gateway per the documentation located here: Use #connections Commands in Your Backend Service
You'll need to give this lambda permission to invoke the API Gateway per the documentation here: Use IAM Authorization
I hope this helps!

getting Task timed out after 59.05 seconds aws lambda nodejs

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

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.');
});
});

How to send http request with nodejs AWS Lambda?

I'm using AWS Lambda to drive an Alexa Skill Kit development. In an attempt to track events, I'd like the script to send an HTTP request on launch, however from the cloud logs it appears as though the http.get function is being skipped during the execution process.
The code is shown below (google.com replaces the analytics tracking url - which has been tested in the browser);
exports.handler = function (event, context) {
var skill = new WiseGuySkill();
var http = require('http');
var url = 'http://www.google.com';
console.log('start request to ' + url)
http.get(url, function(res) {
console.log("Got response: " + res.statusCode);
// context.succeed();
}).on('error', function(e) {
console.log("Got error: " + e.message);
// context.done(null, 'FAILURE');
});
console.log('end request to ' + url);
skill.execute(event, context);
};
The context objects have been commented out to allow for 'skill.execute' to function, yet either way this HTTP request is not executing. Only the 'start' and 'end' console.logs are recorded, those internal in the function do not.
Is this a async issue? Thanks.
You need to make sure the handler is being triggered. There are two ways of accomplishing this:
You could set up a new API endpoint and execute a request on that.
You could hit the Test button and your function would be invoked with the given data.
I copied and pasted your whole snippet except for the first and the last lines (because I don't have customSkill defined anywhere). I was able to get a 200 response code.
In order to successfully complete the http request, the http.get function must be incorporated into a callback function. Else the process will not be completed and will end prematurely, using a callback allows the http request to complete (with or without an error) before continuing with the rest of the function.
WiseGuySkill.prototype.eventHandlers.onLaunch = function (launchRequest, session, response) {
// Call requestFunction to make the http.get call.
// Get response from requestFunction using requestCallback
requestFunction(function requestCallback(err) {
// If error occurs during http.get request - respond with console.log
if (err) {
console.log('HTTP Error: request not sent');
}
ContinueIntent(session,response);
});
};
The function 'requestFunction' calls http.get and fires the callback.
function requestFunction(requestCallback){
var url = "http://www.google.com";
http.get(url, function(res) {
console.log("Got response: " + res.statusCode);
requestCallback(null);
}).on('error', function (e) {
console.log("Got error: ", e);
});
}
And obviously ensure you have required 'http' at the start of the script.
Hope this helps anybody else new to this!

Resources