AWS Lambda get https response after request - javascript

I implemented this AWS Lambda that receives events from slack and response back to slack a sentence and I want to monitor their answer back to the lambda to verify that the message arrived and posted.
// Lambda handler
exports.handler = (data, context, callback) => {
switch (data.type) {
case "url_verification": verify(data, callback); break;
case "event_callback": process(data.event, callback); break;
default: callback(null);
}
};
// Post message to Slack - https://api.slack.com/methods/chat.postMessage
function process(event, callback) {
// test the message for a match and not a bot
if (!event.bot_id && /(aws|lambda)/ig.test(event.text)) {
var text = `<#${event.user}> isn't AWS Lambda awesome?`;
var message = {
token: ACCESS_TOKEN,
channel: event.channel,
text: text
};
var query = qs.stringify(message); // prepare the querystring
https.get(`https://slack.com/api/chat.postMessage?${query}`);
}
callback(null);
}
I want to know how can I get the response of my HTTPS request (that send to me by slack) back to my lambda?

If I understood correctly you want to wait for the result of the your get query.
In your code callback is called immediately and lambda finishes its execution.
To be able to wait for the response you need to remove callback from its current position in the code and call it after request was performed.
// Post message to Slack - https://api.slack.com/methods/chat.postMessage
function process(event, callback) {
// test the message for a match and not a bot
if (!event.bot_id && /(aws|lambda)/ig.test(event.text)) {
var text = `<#${event.user}> isn't AWS Lambda awesome?`;
var message = {
token: ACCESS_TOKEN,
channel: event.channel,
text: text
};
var query = qs.stringify(message); // prepare the querystring
https.get(`https://slack.com/api/chat.postMessage?${query}`, (res, err) => {
if (err) return callback(err);
callback(null);
})
}
// callback was here
}

If you can, use request/request-promise to save some lines of code.
To get the http response in your Lambda Function you just need to wait for the response before calling the Lambda Callback.
Eg.:
var request = require('request-promise');
exports.handler = (event, context, callback) => {
request('https://somedomain.com').then((body) => {
//got the response body
callback(null, body);
});
}
It's the same idea if you're using the https module.

Related

Twilio: Delay SMS message while reacting to call

I'm building a twilio function that reacts to incoming phone calls. After a call is received the function should send a SMS to the caller. However, I would like the SMS to be delayed by a couple minutes. Currently, my function looks like this:
exports.handler = function (context, event, callback) {
// Create a new voice response object
const client = context.getTwilioClient();
const client_number = event.From;
const twilio_number = +xxxx;
client.messages.create({ // Send SMS
to: client_number,
from: twilio_number,
body: "Hello on SMS"
}).then(() => { // When request to send SMS is complete, deal with the caller
let twiml = new Twilio.twiml.VoiceResponse();
twiml.say(""); // respond to voice caller
callback(null, twiml);
})
};
Right now the SMS is sent and then the response to the call is sent. I'm not sure how to delay the message.create. I've been reading this but it doesn't work because I can't deal with the call first and then send the message.
https://www.twilio.com/docs/runtime/quickstart/add-delay
Twilio developer evangelist here.
This is not something you can do with just a Twilio Function. While aLittleSalty's answer will work in a situation where your application and event loop continues to run, like in an Express app that you run, but in a Twilio Function once you call the callback method the event loop is terminated so the delay will be cancelled.
Similarly, Twilio Function execution time is limited to 10 seconds, so you can't delay a message by a couple of minutes even if the above would work.
One idea could be that you send the message once the call has completed. Your example code only includes a <Say> so I will work with that, but you could do this in other ways if the call continues differently.
First, update your initial Function to return just the TwiML, but add a <Redirect> after the <Say> so that once the message completes Twilio sends a new webhook.
exports.handler = function (context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
twiml.say(""); // respond to voice caller
twiml.redirect("/after_say");
callback(null, twiml);
};
Then create another Function at the /after_say path that sends the message and hangs up the call.
exports.handler = function (context, event, callback) {
// Create a new voice response object
const client = context.getTwilioClient();
const client_number = event.From;
const twilio_number = +xxxx;
let twiml = new Twilio.twiml.VoiceResponse();
twiml.hangup();
client.messages.create({ // Send SMS
to: client_number,
from: twilio_number,
body: "Hello on SMS"
}).then(() => {
callback(null, twiml);
}).catch(error => {
console.error(error);
callback(null, twiml);
});
}
That's still not going to give you a couple of minutes between the call coming in and the message being sent, but it will hopefully send once the call is complete and be a better experience.
This doesn't work with Twilio Functions check #philnash answer
You have to return the callback first to answer the call, and then in a delayed promise send the SMS
exports.handler = function (context, event, callback) {
const delay = (delayMS) => new Promise((res) => {
setTimeout(res, delayMS)
})
// Create a new voice response object
const client = context.getTwilioClient();
const client_number = event.From;
const twilio_number = +xxxx;
let twiml = new Twilio.twiml.VoiceResponse();
twiml.say(""); // respond to voice caller
callback(null, twiml);
return (
delay(10000)
.then(() =>
client.messages.create({ // Send SMS
to: client_number,
from: twilio_number,
body: "Hello on SMS"
})
)
.then(() => console.log("SMS sent"))
)
}
Hopefully this helps.
Note: delay is an utility function where you pass the amounts of milliseconds you want to wait.

Redis stream command passing message with parsed value

I am creating the client using Redis and node js, I want to add a message to stream with the below format. I am facing an issue when trying the parse the message with the request body value.
async function userRegister (request) {
var userID = request.body.userID
var username = request.body.userName
redisClient.xadd(
'user:create',
'*',
'module',
`userRegister`,
'request_id',
'2312432434',
'message',
`{"module":"*","user_id":"1","username":"fffff"}`, // this works bcoz its string.
`{"module":"*","userID":"1","username":"here i need pass the above request body value"}`,
function (err, resp) {
if (err) {
console.log(err)
} else {
console.log(resp)
}
}
)
}

Invoke AWS Service across regions

I am trying to use SNS located in São Paulo (sa-east-1) from a lambda function (Node.js 8.10) on Ohio (us-east-2). This is the first time I try to use a AWS service located in another region. So far, this is what I am doing:
//init aws resources
const AWS = require('aws-sdk');
const sns = new AWS.SNS({apiVersion: '2010-03-31', region: 'sa-east-1'});
//promisefy AWS.SNS.createPlatformEndpoint method
snsCreatePlatformEndpoint = params => new Promise(
(resolve, reject)=>{
sns.createPlatformEndpoint(params, function(error, data){
if (error) { reject(error); }
else { resolve(data); }
});
}
);
exports.handler = (awsEvent, context, callback) => {
//parse stuff in here
...
HandleToken(token, callback);
};
async function HandleToken(token, callback){
try{
let params = {
PlatformApplicationArn: process.env.PlatAppArn,
Token: token,
};
console.log('params:', params); // this prints as expected
let {EndpointArn} = await snsCreatePlatformEndpoint(params);
console.log('It should pass through here'); // it is not printed
//returns a success response
...
} catch (error) {
//returns an error response
...
}
}
I have set a really high timeout for my lambda function: 5mins.
I also have tested the same code on a lambda function located in São Paulo(sa-east-1), and it works.
I have been receiving the following error on my client:
"Request failed with status code 504"
"Endpoint request timed out"
Question: How can I use SNS in another AWS region correctly?
You shouldn't need to do any special setup beyond setting the region.
E.g., I use the following pattern to send notifications from us-east-1 to Tokyo (ap-northeast-1):
// this lambda runs in us-east-1
let AWS = require("aws-sdk");
AWS.config.update({ region: "ap-northeast-1" }); // asia-pacific region
exports.handler = async (event, context) => {
var params = {
Message: 'my payload',
TopicArn: 'arn:aws:sns:ap-northeast-1:xxxxxx:tokyoSNS'
};
let SNS = new AWS.SNS({apiVersion: '2010-03-31'});
var data = await SNS.publish(params).promise();
// check if successful then return
}
No endpoints, etc., was setup. Are you required to run your lambda in a VPC? That's the only complication I can think of at the moment.

Facebook Graph API in aws lambda failing

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

Slack slash command delayed response on AWS Lambda

I'm trying to make an integration for Slack that queries a server and gets back some results to the user. The search sometimes takes longer than the window Slack allows for responses, so I need to immediately return status 200.
How can I do this with a lambda function? I tried using the callback function and then sending the data to another lambda service, but the original function waits for it to return, meaning I'm still getting held up by the server I'm querying.
Here's what I'm working with
var rp = require('request-promise');
exports.handler = (event, context, callback) =>{
//I wanted this to send back my STATUS 200 so the command wouldn't time out
callback(null, "Working...");
//I want this post to happen asynchronously so that slack gets the callback response while the search is happening
//but this still waits until the post comes back before resolving the callback
var options = {
method: 'POST',
uri: "https://url-to-other.service",
body:{
"team": event.team,
"label": event.label,
"url": event.aresponse_url
},
json:true
};
rp(options);
};
When I run this, the callback text shows up after the result from the other function, meaning this service is waiting for the other to stop running before returning the status.
That won't work in a single lambda function because as soon as you invoke callback() the lambda container dies. What you could do is have this lambda function invoke another lambda function before calling callback() which in turn will POST to your slack channel url.
Here's an example of how that would work (it's not a 100% but should give you a good idea of how it'll work.)
Function 1: (receive slack event, invoke second function, immediatly return 200
let AWS = require('aws-sdk')
exports.handler = (event, context, callback) => {
let lambda = new AWS.Lambda()
let params = {
FunctionName: 'YOUR_SECOND_FUNCTION_NAME',
InvocationType: 'Event', // Ensures asynchronous execution
Payload: JSON.stringify({
team: event.team,
label: event.label,
url: event.aresponse_url
})
}
return lambda.invoke(params).promise() // Returns 200 immediately after invoking the second lambda, not waiting for the result
.then(() => callback(null, 'Working...'))
}
Function 2: (receive first lambda event, wait for search to complete, POST to slack channel)
let rp = require('request-promise')
exports.handler = (event, context, callback) => {
let searchOptions = {
method: 'POST',
uri: 'https://url-to-other.service',
body: {
'team': event.team,
'label': event.label,
'url': event.aresponse_url
},
json:true
}
return rp(searchOptions)
.then(result => {
let slackOptions = {
method: 'POST',
uri: 'YOUR_SLACK_CHANNEL_URL',
body: {
text: JSON.stringify(result) // I stringified the search result for you for ease's sake, not sure what you need here
},
json: true
}
return rp(slackOptions)
})
.then(() => { callback(null, 'good measure') })
}

Categories

Resources