I'm trying to call external API inside aws Lambda function using node request Module. so far I'm success of calling API and get the data within lambda execution. only problem i'm having is getting my userInfo data with response.even my userInfo has data in Giving me empty Object in client side
var AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
var request = require('request');
const encode = require('nodejs-base64-encode');
var lambda = new AWS.Lambda();
import { Handler, Context, Callback } from "aws-lambda";
import { PayPalLinkDetails } from "../../View/PayPalLinkDetails";
import { PayPalLinkResponse, PayPalLinkResponseBody } from "../../View/PayPalLinkResponseBody";
const PAYPAL_CLIENT = process.env.PayPalClientID;
const PAYPAL_SECRET = process.env.PayPalSecretKEY;
const PAYPAL_OAUTH_API = process.env.PayPalAuthAPI;
const PAYPAL_IDENTITY_API = process.env.PayPalIdentityAPI;
const LinkPayPal: Handler = async (paypalRequest : PayPalLinkDetails, context: Context, callback: Callback) => {
var userInfo = new PayPalLinkResponse();
var paypalresponse = new PayPalLinkResponseBody();
const basicAuth = encode.encode(PAYPAL_CLIENT+":"+PAYPAL_SECRET, 'base64');
var options = {
'method': 'POST',
'url': PAYPAL_OAUTH_API,
'headers': {
'Authorization': 'Basic '+basicAuth,
'Content-Type': 'application/x-www-form-urlencoded'
},
form: {
'grant_type': 'authorization_code',
'code': paypalRequest.code
}
};
await request(options, async function (error : any, response :any) {
if (error)
{
console.log(error);
}
else
{
paypalresponse = response.body;
// save data to DB here
}
});
var getIdentity = {'method': 'get','url': PAYPAL_IDENTITY_API,'headers': {'Authorization': 'Basic '+basicAuth,'Content-Type': 'application/x-www-form-urlencoded'},form: {'grant_type': 'authorization_code','code': paypalresponse.access_token}};
await request(getIdentity, function (err : any, res :any)
{
if (err)
{
console.log(err);
}
else
{
userInfo = res.body; // this Print the values as expected
console.log(userInfo);
}
});
callback(null,userInfo); // This Giving me Empty value
}
export {LinkPayPal}
i think i'm calling callback in wrong way. is there any suggestions for solve this issue ..?
The problem is that you have mixed up callback and async/await style which wouldn't work the way you expect it to be. You have couple of choices here
[Not Recommended]: Do a nested callback and on response of first callback, call second request and so on.
[Not Recommended]: Use a promise version of the request package which is called request-promise as this is now being deprected.
[Not Recommended]: Convert request's callback style to promise based by wraping up in promise. Again request module is being deperecated. See here for more details.
[Recommended]: Use some modern day packages which supports promises out of the box and maintained properly. Like got, axios etc. You can see the list here.
This is how the code will look if you use let's say got pacakge to make http calls.
var AWS = require("aws-sdk");
AWS.config.region = "us-east-1";
var got = require("got");
const encode = require("nodejs-base64-encode");
var lambda = new AWS.Lambda();
import { Handler, Context, Callback } from "aws-lambda";
import { PayPalLinkDetails } from "../../View/PayPalLinkDetails";
import {
PayPalLinkResponse,
PayPalLinkResponseBody
} from "../../View/PayPalLinkResponseBody";
const PAYPAL_CLIENT = process.env.PayPalClientID;
const PAYPAL_SECRET = process.env.PayPalSecretKEY;
const PAYPAL_OAUTH_API = process.env.PayPalAuthAPI;
const PAYPAL_IDENTITY_API = process.env.PayPalIdentityAPI;
const LinkPayPal: Handler = async (
paypalRequest: PayPalLinkDetails,
context: Context,
callback: Callback
) => {
var userInfo = new PayPalLinkResponse();
var paypalresponse = new PayPalLinkResponseBody();
const basicAuth = encode.encode(
PAYPAL_CLIENT + ":" + PAYPAL_SECRET,
"base64"
);
var options = {
method: "POST",
url: PAYPAL_OAUTH_API,
headers: {
Authorization: "Basic " + basicAuth,
"Content-Type": "application/x-www-form-urlencoded"
},
form: {
grant_type: "authorization_code",
code: paypalRequest.code
}
};
const paypalresponse = await got(options);
var getIdentity = {
method: "get",
url: PAYPAL_IDENTITY_API,
headers: {
Authorization: "Basic " + basicAuth,
"Content-Type": "application/x-www-form-urlencoded"
},
form: {
grant_type: "authorization_code",
code: paypalresponse.access_token
}
};
const userInfo = await got(getIdentity);
return userInfo;
};
export { LinkPayPal };
You might need to tweak the options as per the got style but you will get an idea.
I've trying to call lambda function from another lambda function and get result to execute rest of the lambda.
Basic flow of function is below
X - main lambda function
- process A (independent)
- process C (need input from process B)
- process D
- return final dataset
Y - Child lambda function
- process B ( need input from process A and respond back to X )
This is my code so far
var AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
var lambda = new AWS.Lambda();
const GetUserCheckoutData: Handler = async (userRequest: EmptyProjectRequest, context: Context, callback: Callback) => {
const dboperation = new UserController();
const usercheckoutdata = new CheckOutInfo();
const addresscontroller = new AddressController();
const ordercontroller = new OrderController();
const paypalcreateorder = new PayPalController();
const userid = await dboperation.getUserID(userRequest.invokeemailAddress);
usercheckoutdata.useraddressdetails = await addresscontroller.GetListOfAddressByUserID(userid);
var orderlist = new Array<Order>();
orderlist = [];
orderlist = await ordercontroller.GetCurrentOrder(userid);
console.log("Order Complete");
var params = {
FunctionName: 'api-ENGG-SellItem', // the lambda function we are going to invoke
InvocationType: 'RequestResponse',
LogType: 'Tail',
Payload: '{ "orderlist" : xxxxxxx }'
};
lambda.invoke(params, (err:any, res:any) => {
if (err) {
callback(err);
}
console.log(JSON.stringify(res));
callback(null, res.Payload);
});
usercheckoutdata.orderID = await paypalcreateorder.CreateOrder(userid , orderlist);
usercheckoutdata.orderPreview = await ordercontroller.OrderPreview(userid);
//callback(null,usercheckoutdata);
};
export { GetUserCheckoutData }
I tried a few different ways but flow is not working properly. cross lambda function is executing. but cannot get the response on time.
My child lambda function demo code
import { Handler, Context } from "aws-lambda";
const SellItem: Handler = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
console.log("Other Lambda Function");
setTimeout(() => {
callback(null, "My name is Jonathan");
}, 1000 * 10); // 10 seconds delay
}
export {SellItem}
I think since I don't have much NodeJS knowledge this is happening. don't know how to put call back in right way I guess. Any help will be appreciated
You should make your call to the second lambda a promise, so you can await it.
const res = await lambda.invoke(params).promise();
// do things with the response
I am trying to send a SMS message using AWS Pinpoint to a specific phone number.
Here is what I have so far in nodejs:
var AWS = require('aws-sdk');
AWS.config.update({region: 'us-east-1'});
var pinpoint = new AWS.Pinpoint({apiVersion: '2016-12-01'});
pinpoint.sendMessages(XXX);
I am very confused by what needs to go into XXX. https://docs.aws.amazon.com/cli/latest/reference/pinpoint/send-messages.html has a long input. Where does the phone number go? A simple example would be greatly appreciated.
This is what finally worked. [Telephone] is the number, for example [15553451234]:
var AWS = require('aws-sdk');
// Set region
AWS.config.update({region: 'us-east-1'});
var pinpoint = new AWS.Pinpoint({apiVersion: '2016-12-01'});
var params = {
ApplicationId: 'ecba683ea3ee4af1bba3176a70ac1e71',
MessageRequest : {
Addresses : {
[telephone] : {
"BodyOverride": message,
"ChannelType": "SMS",
}
},
MessageConfiguration : {
SMSMessage:
{
Body : message,
MessageType : "TRANSACTIONAL"
}
}
}
};
var publishTextPromise = await pinpoint.sendMessages(params).promise();
This is what we did to handle the answer, stores in DynamoDB:
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();
exports.handler = async (event) => {
// TODO implement
console.log(JSON.stringify(event));
var pinpointResponse = JSON.parse(event.Records[0].Sns.Message);
var phoneNumber = pinpointResponse.originationNumber.substring(2);
var message = pinpointResponse.messageBody;
console.log("phoneNumber", phoneNumber);
console.log("message", message);
//Insert into DynamoDB
var InsertParams = {
TableName : "ChatHistory",
Item : {
"phoneNumber" : phoneNumber + "",
"Answer" : message
}
};
var AWSNew = require('aws-sdk');
AWSNew.config.update({region: 'us-east-2'});
var docClient = new AWSNew.DynamoDB.DocumentClient();
await docClient.put(InsertParams).promise();
const response = {
statusCode: 200,
body: JSON.stringify('SUCCESS'),
};
return response;
};
I have written Lambda function using JavaScript, which responses to my voice and turns on the LED on my Raspberry.
But I have a problem with publishing its state to my thing topic. While Alexa responses correct ("Turning on" if Im asking to turn it on and "Turning off" if asking to off), my topic doesn't always get the state changes. Some times it gets data and sometime it doesn't and after few more invocations it gets data in bulk, and I cant even get the logic of creating a sequence of data in that bulk.
var AWS = require('aws-sdk');
var config = {};
config.IOT_BROKER_ENDPOINT = "xxxxxx.iot.us-east-1.amazonaws.com";
config.IOT_BROKER_REGION = "us-east-1";
config.IOT_THING_NAME = "raspberry";
var iotData = new AWS.IotData({endpoint: config.IOT_BROKER_ENDPOINT});
var topic = 'LED';
exports.handler = function (event, context) {
...
...
function updatePowerState (intent, session, callback) {
var speechOutput = '';
var newValue = '';
var repromptText = '';
const cardTitle = 'Power';
var sessionAttributes = {};
const shouldEndSession = true;
var value = intent.slots.PowerState.value;
if(value == 'on' || value == 'off') {
newValue = value.toUpperCase();
speechOutput = 'Turning your lamp ' + value;
updateShadow(newValue);
} else {
speechOutput = 'I didnt understand you. Please, repeat your request.';
}
callback(sessionAttributes, buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
function updateShadow(newValue) {
let payload = {
state: {
desired: {
power_state: newValue
}
}
};
var JSON_payload = JSON.stringify(payload);
var updates = {
topic: topic,
payload: JSON_payload,
qos: 0
};
iotData.publish(updates, (err, data) => {
if(err) {
console.log(err);
}
else {
console.log('Success!');
}
});
}
Do you have any ideas about its causes? Thank you!
Async methods like iotData.publish cause problems into AWS Lambda, because you request a execution and the lambda function ends soon without waiting for the response and processing the request.
Another problem could be your permissions.
var AWS = require('aws-sdk');
var iotdata = new AWS.IotData({endpoint: 'iotCoreEndpoint.iot.us-east-1.amazonaws.com'});
exports.handler = async (event) => {
var params = {
topic: 'topic/topicName',
payload: JSON.stringify(event.body),
qos: 0
};
await iotdata.publish(params).promise()
};
Just make sure to add the required permissions or you can attach the following policy to your lambda role: AWSIoTWirelessFullPublishAccess
We are working on an Alexa skill and it will need to reach out to external REST API's to get data. I'm having a really hard time getting this to work in our lambda function for some reason. I'm also having a hard time determining if the problem is in my node.js code not using callback's correctly or if it's in the VPC settings for my function. Here is my code, I've stripped out the non-essential stuff.
/* eslint-disable func-names */
/* eslint quote-props: ["error", "consistent"]*/
/**
* This sample demonstrates a simple skill built with the Amazon Alexa Skills
* nodejs skill development kit.
* This sample supports multiple lauguages. (en-US, en-GB, de-DE).
* The Intent Schema, Custom Slots and Sample Utterances for this skill, as well
* as testing instructions are located at https://github.com/alexa/skill-sample-nodejs-fact
**/
'use strict';
const Alexa = require('alexa-sdk');
const APP_ID = undefined; // TODO replace with your app ID (OPTIONAL).
const https = require('https');
const handlers = {
'LaunchRequest': function () {
this.emit('GetFact');
},
'GetNewFactIntent': function () {
this.emit('GetFact');
},
'maintenanceIntent': function () {
console.log('inside maintenanceIntent');
var options = {
host: 'api.forismatic.com',
path: '/api/1.0/?method=getQuote&lang=en&format=text',
method: 'GET'
};
getQuote(options, function (quote){
if(quote === ''){
console.log("No quote");
//speechOutput = "Please try again later";
}
else{console.log(quote)}
//self.emit(':tellWithCard', speechOutput, SKILL_NAME, text);
});
// Create speech output
// Place holder
var randomFact = 'Test Fact';
const speechOutput = randomFact;
this.emit(':tellWithCard', speechOutput, 'test skill name', randomFact);
},
'AMAZON.HelpIntent': function () {
const speechOutput = this.t('HELP_MESSAGE');
const reprompt = this.t('HELP_MESSAGE');
this.emit(':ask', speechOutput, reprompt);
},
'AMAZON.CancelIntent': function () {
this.emit(':tell', this.t('STOP_MESSAGE'));
},
'AMAZON.StopIntent': function () {
this.emit(':tell', this.t('STOP_MESSAGE'));
},
};
exports.handler = function (event, context, callback) {
const alexa = Alexa.handler(event, context);
alexa.APP_ID = APP_ID;
// To enable string internationalization (i18n) features, set a resources object.
//alexa.resources = languageStrings;
alexa.registerHandlers(handlers);
alexa.execute();
};
function getQuote(options, callback){
var text = '';
console.log("in getquote");
https.get(options, function(res) {
console.error("Got response: " + res.statusCode);
res.on("data", function(chunk) {
console.error("BODY: " + chunk);
text = '' + chunk;
return callback(text);
});
}).on('error', function(e) {
text = 'error' + e.message;
console.error("Got error: " + e.message);
});
}
Now when I invoke the maintenanceIntent this is what I see in the logs.
{"timestamp":1508426249817,"message":"START RequestId: 9f66123e-b4e0-11e7-baac-1bfb01d2abc8 Version: $LATEST","logStream":"2017/10/19/[$LATEST]0e048ab2fc5441cda8007e4a1963bf02","logGroup":"/aws/lambda/factDemo","requestID":"9f66123e-b4e0-11e7-baac-1bfb01d2abc8"}
{"timestamp":1508426250256,"message":"Warning: Application ID is not set","logStream":"2017/10/19/[$LATEST]0e048ab2fc5441cda8007e4a1963bf02","logGroup":"/aws/lambda/factDemo","requestID":"9f66123e-b4e0-11e7-baac-1bfb01d2abc8"}
{"timestamp":1508426250256,"message":"inside maintenanceIntent","logStream":"2017/10/19/[$LATEST]0e048ab2fc5441cda8007e4a1963bf02","logGroup":"/aws/lambda/factDemo","requestID":"9f66123e-b4e0-11e7-baac-1bfb01d2abc8"}
{"timestamp":1508426250256,"message":"in getquote","logStream":"2017/10/19/[$LATEST]0e048ab2fc5441cda8007e4a1963bf02","logGroup":"/aws/lambda/factDemo","requestID":"9f66123e-b4e0-11e7-baac-1bfb01d2abc8"}
{"timestamp":1508426250256,"message":"END RequestId: 9f66123e-b4e0-11e7-baac-1bfb01d2abc8","logStream":"2017/10/19/[$LATEST]0e048ab2fc5441cda8007e4a1963bf02","logGroup":"/aws/lambda/factDemo","requestID":"9f66123e-b4e0-11e7-baac-1bfb01d2abc8"}
{"timestamp":1508426250256,"message":"REPORT RequestId: 9f66123e-b4e0-11e7-baac-1bfb01d2abc8\tDuration: 378.28 ms\tBilled Duration: 400 ms \tMemory Size: 128 MB\tMax Memory Used: 33 MB\t","logStream":"2017/10/19/[$LATEST]0e048ab2fc5441cda8007e4a1963bf02","logGroup":"/aws/lambda/factDemo","requestID":"9f66123e-b4e0-11e7-baac-1bfb01d2abc8"}
So I can see it's actually calling the getQuote function. I'm not seeing any errors or success messages at all. I thought maybe I wasn't using callbacks correctly (node isn't my normal development language) but I've actually pulled code straight from an Amazon example on GitHub and I couldn't even get that to work. (This code is very similar to it except it's a little shorter.)
If I strip this down and run it locally through the node, it works fine.
As far as the networking stuff, I followed this guide: https://gist.github.com/reggi/dc5f2620b7b4f515e68e46255ac042a7
. I've also tried Amazon guides but at this point, I'm not even sure how I would check internet connectivity or if this is even the problem.
Any help to get on the right track would be greatly appreciated!
--EDIT--
I've changed my code as so. This comes straight from the alexa-cookbook at https://raw.githubusercontent.com/alexa/alexa-cookbook/master/external-calls/httpsGet/src/index.js
/* eslint-disable func-names */
/* eslint quote-props: ["error", "consistent"]*/
/**
* This sample demonstrates a simple skill built with the Amazon Alexa Skills
* nodejs skill development kit.
* This sample supports multiple lauguages. (en-US, en-GB, de-DE).
* The Intent Schema, Custom Slots and Sample Utterances for this skill, as well
* as testing instructions are located at https://github.com/alexa/skill-sample-nodejs-fact
**/
'use strict';
const Alexa = require('alexa-sdk');
const APP_ID = undefined; // TODO replace with your app ID (OPTIONAL).
const https = require('https');
const handlers = {
'LaunchRequest': function () {
this.emit('GetFact');
},
'GetNewFactIntent': function () {
this.emit('GetFact');
},
'maintenanceIntent': function () {
console.log('inside maintenanceIntent');
var myRequest = 'Florida';
httpsGet(myRequest, (myResult) => {
console.log("sent : " + myRequest);
console.log("received : " + myResult);
this.response.speak('The population of ' + myRequest + ' is ' + myResult);
this.emit(':responseReady');
}
);
// Create speech output
// Place holder
var randomFact = 'Test Fact';
const speechOutput = randomFact;
this.emit(':tellWithCard', speechOutput, 'test skill name', randomFact);
},
'AMAZON.HelpIntent': function () {
const speechOutput = this.t('HELP_MESSAGE');
const reprompt = this.t('HELP_MESSAGE');
this.emit(':ask', speechOutput, reprompt);
},
'AMAZON.CancelIntent': function () {
this.emit(':tell', this.t('STOP_MESSAGE'));
},
'AMAZON.StopIntent': function () {
this.emit(':tell', this.t('STOP_MESSAGE'));
},
};
exports.handler = function (event, context, callback) {
console.log("exports handler");
const alexa = Alexa.handler(event, context);
alexa.APP_ID = APP_ID;
// To enable string internationalization (i18n) features, set a resources object.
//alexa.resources = languageStrings;
alexa.registerHandlers(handlers);
alexa.execute();
console.log("post execute");
};
function httpsGet(myData, callback) {
// GET is a web service request that is fully defined by a URL string
// Try GET in your browser:
// https://cp6gckjt97.execute-api.us-east-1.amazonaws.com/prod/stateresource?usstate=New%20Jersey
console.log("in");
console.log(myData);
// Update these options with the details of the web service you would like to call
var options = {
host: 'cp6gckjt97.execute-api.us-east-1.amazonaws.com',
port: 443,
path: '/prod/stateresource?usstate=' + encodeURIComponent(myData),
method: 'GET',
// if x509 certs are required:
// key: fs.readFileSync('certs/my-key.pem'),
// cert: fs.readFileSync('certs/my-cert.pem')
};
var req = https.request(options, res => {
res.setEncoding('utf8');
var returnData = "";
console.log("request");
res.on('data', chunk => {
console.log("data");
returnData = returnData + chunk;
});
res.on('end', () => {
console.log("end");
// we have now received the raw return data in the returnData variable.
// We can see it in the log output via:
// console.log(JSON.stringify(returnData))
// we may need to parse through it to extract the needed data
var pop = JSON.parse(returnData).population;
callback(pop); // this will execute whatever function the caller defined, with one argument
});
});
console.log("req.end");
req.end();
}
Same idea but slightly different execution of getting the result from the endpoint. This is the log output.
{"timestamp":1508434982754,"message":"START RequestId: f4a39351-b4f4-11e7-a563-fbf7599fa72f Version: $LATEST","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434982887,"message":"exports handler","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434982887,"message":"Warning: Application ID is not set","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434982887,"message":"inside maintenanceIntent","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434982887,"message":"in","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434982887,"message":"Florida","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434983307,"message":"req.end","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434983309,"message":"post execute","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434983367,"message":"END RequestId: f4a39351-b4f4-11e7-a563-fbf7599fa72f","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
{"timestamp":1508434983367,"message":"REPORT RequestId: f4a39351-b4f4-11e7-a563-fbf7599fa72f\tDuration: 608.20 ms\tBilled Duration: 700 ms \tMemory Size: 128 MB\tMax Memory Used: 35 MB\t","logStream":"2017/10/19/[$LATEST]3252e394be9b4a229c3a0d042deffbf8","logGroup":"/aws/lambda/factDemo","requestID":"f4a39351-b4f4-11e7-a563-fbf7599fa72f"}
I've tried this in the VPC and out of the VPC with the same result.
So this is solved thanks to this question: Node JS callbacks with Alexa skill
Particurlarly, this line
The :tell function will call the lambda callback and terminate the execution of the lambda function.
Moving the this.emit outside the httpsGet into it like so fixed the issue.
httpsGet(myRequest, (myResult) => {
console.log("sent : " + myRequest);
console.log("received : " + myResult);
this.response.speak('The population of ' + myRequest + ' is ' + myResult);
this.emit(':responseReady');
// Create speech output
// Place holder
var randomFact = 'Test Fact';
const speechOutput = randomFact;
this.emit(':tellWithCard', speechOutput, 'test skill name', randomFact);
}
);