AWS Lambda javascript-sdk , implementing dynamodb putItem method receive timeout - node.js

I have tried on both Async Handlers and Non-Async Handlers ways,
to implement dynamodb putItem method. it returned an invalid response.
Its works for commented setTimeout function.
I tried by promisify the dynomodb putItem method in Non-Async Handler way as mentioned and official documentation https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html. But still no luck.
can some one point me out the issue on code?
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({
region: 'ap-south-1',
apiVersion: '2012-08-10'
});
exports.lambdaHandler = async function(event) {
const promise = new Promise(function(resolve, reject){
// setTimeout(function(){
// console.log("resolvedx")
// resolve({statusCode: 200, body: "resolvedx"})
// }, 200)
dynamodb.putItem({
TableName: 'CUSTOMER_LIST',
Item: {
'CUSTOMER_ID': { N: '012' },
'CUSTOMER_NAME': { S: 'zzzza' }
}
}
, function(err, data) {
if (err) {
console.log("err", err);
reject(JSON.stringify({statusCode:200, body:err}));
} else {
console.log("success", data);
resolve(JSON.stringify({statusCode:200, body:data}));
}
})
})
return promise
}
as Non-Async Handlers
exports.lambdaHandlerx = (event, context, callback) => {
const dbPromise = () => new Promise((resolve, reject) => {
console.log("inside db promise")
dynamodb.putItem({
TableName: 'CUSTOMER_LIST',
Item: {
'CUSTOMER_ID': { N: '023' },
'CUSTOMER_NAME': { S: 'asdddwa' }
}
}
, function(err, data) {
if (err) {
console.log("err", err);
reject(err);
} else {
console.log("success", data);
resolve(data);
}
})
})
Promise.all([dbPromise()]).then(data => {
console.log("then", data)
callback(null, {
'statusCode': 200,
'body': JSON.stringify({
message: "dataxz"+ JSON.stringify(data)
})
})
}
).catch(e => {
callback(null, {
'statusCode': 200,
'body': JSON.stringify({
message: 'err'
})
})
})
}
Output
Function returned an invalid response (must include one of: body, headers, multiValueHeaders or statusCode in the response object). Response received:

Related

node.js variable not surviving code block

I'm experimenting with Node.js in AWS Lambda. And, I've run into a problem with the code below. Result value and error value are always returned blank. I'm pretty sure this is just a scope issue I'm to tired to see. How do I capture the return value and pass it back in the response? I'm sure the programs is connecting to redis because I get an error message if I change the port or URL and I don't when they're set properly.
The return code:
{
"statusCode": 200,
"body": "{\"key\":\"q1\"}"
}
The program code:
const Redis = require("ioredis");
const redis = new Redis(6379, 'fredflenstone.lhpxwy.az.0002.use2.cache.amazonaws.com');
exports.handler = async(event)=>{
let key=event.key;
let response;
let resultValue;
let errorValue;
redis.get(key, (err, result) => {
if (err) {
errorValue=err;
} else {
resultValue=result;
}
});
response={
key: key,
resultValue: resultValue,
errorValue: errorValue
};
return {
statusCode: 200,
body: JSON.stringify(response)
};
};
The problem is due to promises. Your handler execution is completing before redis is returning the result. Following snippet should work:
const Redis = require("ioredis");
const redis = new Redis(6379, 'fredflenstone.lhpxwy.az.0002.use2.cache.amazonaws.com');
exports.handler = async(event)=>{
let key=event.key;
let response;
let resultValue;
let errorValue;
try{
resultValue = await redis.get(key);
}catch(error) {
errorValue = error;
}
response={
key: key,
resultValue: resultValue,
errorValue: errorValue
};
return {
statusCode: 200,
body: JSON.stringify(response)
};
};
It is because your call to "redis.get" is not resolved when "response" is sent.
You need to wait for the response :
await new Promise((resolve) => {
redis.get(key, (err, result) => {
if (err) {
errorValue=err;
} else {
resultValue=result;
}
resolve();
});
})
or even better turn the redis response into a promise response :
await new Promise((resolve, reject) => {
redis.get(key, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
})
})
.then((result) => resultValue = result)
.catch((err) => errorValue = err)

how to wait for a result in rxjs for a lambda function

I am trying to write an async lambda function which is calling a function for sign up a user in cognito.
my problem is that in my lambda function, it is not waiting for the result and finish the execution. would you mind check what is my issue? I am new to rxjs. please help me.
mylambda function
exports.handler = async (event, context) => {
//poolData and params will fetch from event
let source = await signup(poolData, params);
console.log(source);
});
my signup function
function signup(poolData, body) {
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
const { username, password, attributes } = body;
const attributesList = [];
if (Array.isArray(attributes)) {
attributesList.push(
...attributes.map(item => new AmazonCognitoIdentity.CognitoUserAttribute(item))
);
}
let source = Observable.create(observer => {
let output = (err, res) => {
if (err)
{
observer.error(err);
}
else
{
const cognitoUser = res.user;
const data = {
username: cognitoUser.getUsername(),
};
observer.next(data);
}
observer.complete();
}
userPool.signUp(username, password, attributesList, null, output);
});
let respond;
let subscriber = {
next(value) {
console.log('Subscriber - next: ', value);
respond = {
'statusCode': 200,
'body': JSON.stringify({
"username": value.username,
})
}
}, error(err) {
console.log('Subscriber - err: ', err);
respond = err;
},
complete() {
console.log('Subscriber - complete');
return response;
}
};
source.subscribe(subscriber);
}
module.exports = signup;
This behavior is totally normal.
So first thing first, an observable is not a promise which means you are not able to await a response with the await keyword, also I don't see anything to be returned from the signup function, which will probably lead to undefined to be logged anyways.
So how to fix that, one way to fix this issue is to use toPromise() which will turn your observable into a promise which then can be awaited wherever needed.
The other way (which is the rxjs way) will be to return from the signup function the observable and inside your handler function to subscribe for the response.
let subscriber = {
next(value) {
console.log('Subscriber - next: ', value);
respond = {
'statusCode': 200,
'body': JSON.stringify({
"username": value.username,
})
}
}, error(err) {
console.log('Subscriber - err: ', err);
respond = err;
},
complete() {
console.log('Subscriber - complete');
return response;
}
};
exports.handler = (event, context) => {
//poolData and params will fetch from event
signup(poolData, params).subscribe(subscriber);
})

Request Promise unhandled reject

Basically I have to do a POST request an try to save this data on an API after I get the return of this POST and save this information on a database.
So, I did a call to a promise (saveProcessedData or saveErrorData) inside another promise, works as expected although is trowing out a warning:
Unhandled rejection StatusCodeError: 400
const sendData = async(data, item) => {
let payload = [],
dataProcessed = [];
let options = {
method: data.endpoint.method,
url: api._url + data.endpoint.url,
headers:
{
'Postman-Token': api._postmanToken,
'cache-control': api._cache,
'x-ccasset-language': 'en',
Authorization: data.token
},
body: item,
json: true
};
return new Promise((resolve, reject) => {
api._request(options, async (error, response, body) => {
if(!response.body.errorCode && response.statusCode === 200) {
payload = {
body: response.body,
type: data.req.body.type
}
dataProcessed = await db.saveProcessedData(payload);
} else {
payload = {
body: item,
type: data.req.body.type,
description: response.body.message
}
dataProcessed = await db.saveErrorData(payload);
}
if (error) {
reject(error)
}
resolve(dataProcessed);
});
});
}
How Can I catch this error?
It's better if you don't mix callbacks & promises the way you're doing it. You're getting that error because you're not handling the errors correctly inside api._request callback.
Wrap your code inside the callback in a try/catch, because that's how you handle exceptions in async functions.
new Promise(async resolve => {
await Promise.reject() // Unhandled Promise Rejection
}).catch(console.error) // not catched
Should be:
new Promise(async resolve => {
try {
await Promise.reject()
resolve();
} catch(e) {
reject(e)
}
}).catch(console.error) // catched
In any case, since you're wrapping api._request in a Promise, it would be better to do:
const sendData = async(data, item) => {
// ...
const { error, response, body } = await new Promise((resolve, reject) => {
api._request(options, (error, response, body) => resolve({ error, response, body }))
})
if(!response.body.errorCode && response.statusCode === 200) {
payload = {
body: response.body,
type: data.req.body.type
}
dataProcessed = await db.saveProcessedData(payload);
} else {
payload = {
body: item,
type: data.req.body.type,
description: response.body.message
}
dataProcessed = await db.saveErrorData(payload);
}
return dataProcessed;
}
And attach a .catch handler to .sendData
Aside from that in your code, if error is truthy, you're rejecting and then resolving, nothing wrong will happen in that case, but it's better to use return reject(). A Promise, can't be rejected & resolved.
if (error) {
return reject(error)
}
resolve(dataProcessed);
If you're using request, you can use request-promise or request-promise-native which are Promise wrappers around request package.
Just formatting
const sendData = async (data, item) => {
const options = {
method: data.endpoint.method,
url: api._url + data.endpoint.url,
headers: {
'Postman-Token': api._postmanToken,
'cache-control': api._cache,
'x-ccasset-language': 'en',
Authorization: data.token
},
body: item,
json: true
};
return Promise.resolve()
.then(() =>
new Promise((resolve, reject) => {
api._request(options, (error, response) => {
if (!response.body.errorCode && response.statusCode === 200) {
return resolve(
{
body: response.body,
type: data.req.body.type
}
);
}
return reject({
body: item,
type: data.req.body.type,
description: error || response.body.message
});
})
}))
.then(payload => db.saveProcessedData(payload))
.catch(payload => db.saveErrorData(payload))
}
When you call sendData() you can use Promise.prototype.catch() to catch exceptions that occurred in your promise, including any reject():
sendData(myData, myItem)
.then(result => {
//do stuff with result
}
.catch(error => {
//handle error
};

Need to scan db and either display a message or update item

I'm using Dynamo DB in AWS. I've reviewed some of the documentation and now have a semi functional lambda function. I'm not too familiar with nodejs but figured "it's javascript! just treat it like that!".
So this function is about scanning the database in the user_handle db and if a user already has an existing handle, then alert the user that the handle already exists. If the handle does not exist, then update the user_handle item.
So my function updates the handle if it doesn't exist, but if it does, then I need to see this in my response.
Here's my lambda function:
const AWS = require('aws-sdk'); const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-west-1'});
exports.handler = (event, context, callback) => {
let e = JSON.parse(event.body)
var params = {
TableName: event.stageVariables.user,
Key: { 'userId' : e.userId },
UpdateExpression: 'set user_handle = :user_handle',
ExpressionAttributeValues: {
':user_handle' : e.user_handle,
}
};
var scanParams = {
TableName : event.stageVariables.user,
FilterExpression : 'user_handle = :user_handle',
ExpressionAttributeValues : {':user_handle' : e.user_handle}
};
docClient.scan(scanParams, function(err, data) {
if (err) {
console.log("ERROR:", err);
let response = {
"statusCode": err.statusCode,
"headers": {},
"body": JSON.stringify(err)
}
console.log("RESPONSE", response)
callback(response)
} else {
let response = {
"statusCode": 200,
"body": JSON.stringify({"Success": true})
}
callback(null, response)
// console.log("RESPONSE", response)
// console.log("DATA", data)
if( data.Count >= 1 ){
let handleExistsResponse = {
"statusCode": 200,
"body": JSON.stringify({"Success": false})
}
console.log("HANDLE IT", handleExistsResponse)
callback(null, handleExistsResponse)
} else {
docClient.update(params, function(err, data) {
if (err) {
console.log("ERROR:", err);
let response = {
"statusCode": err.statusCode,
"headers": {},
"body": JSON.stringify(err)
}
console.log(response)
callback(response)
} else {
let response = {
"statusCode": 200,
"body": JSON.stringify({"Success": true})
}
callback(null, response)
}
});
}
}
// console.log("DATA", data);
});
};
Here's the DB function in my React Native App:
export async function createUserHandle() {
return new Promise((resolve, reject) => {
let { auth } = store.getState()
let reqBody = {
userId: auth.user.username,
user_handle: auth.user_handle,
}
let path = '/u/create-user-handle'
let myInit = {
headers: { 'Content-Type': 'application/json' },
body: reqBody,
// response: true,
}
API.post(apiName, path, myInit)
.then((resp) => {
console.log('response from user handle', resp)
resolve(resp)
})
.catch((error) => {
console.warn('Create USER Handle ERROR', error)
reject(error)
})
})
}
In the Lambda I was hoping that if (data.Count >= 1){} can give me a response of false coming from
let handleExistsResponse = {
"statusCode": 200,
"body": JSON.stringify({"Success": false})
}
and if it is false, then I can display the appropriate message on the front end. but the response i'm getting is true if i console log it on the front end.
( also, wouldn't mind a code review on this, probably my 3rd time writing a function like this. thanks!)
Figured it out..
} else {
let response = {
"statusCode": 200,
"body": JSON.stringify({"Success": true})
}
callback(null, response)
// console.log("RESPONSE", response)
// console.log("DATA", data)
if( data.Count >= 1 ){
The callback happening after the response kept the next call back after the condition to go through. I'm now getting the response I needed. Feels good :D
Still wouldn't mind a code review :)

SQS to Lambda + SES

I'm new with Lambda & SQS and I'm trying to create a function to send emails, queued in an SQS service, but I don't understand how to call the process function that contains the send + delete queue methods.
Here bellow I paste my code:
'use strict';
const AWS = require('aws-sdk');
const SQS = new AWS.SQS({ apiVersion: '2012-11-05' });
const Lambda = new AWS.Lambda({ apiVersion: '2015-03-31' });
const ses = new AWS.SES({ accessKeyId: "xxxxxxxx", secretAccesskey: "xxxxxxx/xxxxxxxxx" });
const s3 = new AWS.S3({ apiVersion: "2006-03-01", region: "us-west-2" });
const QUEUE_URL = 'https://sqs.us-west-2.amazonaws.com/xxxxxxx/queue';
const PROCESS_MESSAGE = 'process-message';
function getPieceOfMail (path, mapObj, replace) {
return new Promise(function (resolve, reject) {
s3.getObject({
Bucket: "myBucket",
Key: "myKey/" + path
}, function (err, data) {
if (err) {
reject(err);
} else {
if (replace === true) {
var re = new RegExp(Object.keys(mapObj).join("|"), "gi");
data = data.Body.toString().replace(re, function (matched) {
return mapObj[matched.toLowerCase()];
});
resolve(data);
} else {
resolve(data.Body.toString());
}
}
});
});
}
function getRegisterSource (nickname, activate_link) {
var activate_link, pieces;
pieces = [
getPieceOfMail("starts/start.html", {}, false),
getPieceOfMail("headers/a.html", {}, false),
getPieceOfMail("footers/a.html", {}, false),
];
return Promise.all(pieces)
.then(function (data) {
return (data[0] + data[1] + data[2]);
})
.catch(function (err) {
return err;
});
}
function sendEmail (email, data) {
return new Promise(function (resolve, reject) {
var params = {
Destination: { ToAddresses: [email] },
Message: {
Body: {
Html: {
Data: data
},
Text: {
Data: data
}
},
Subject: {
Data: "myData"
}
},
Source: "someone <noreply#mydomain.co>",
};
ses.sendEmail(params, function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
function process(message, callback) {
console.log(message);
// process message
getRegisterSource(event['nickname'], event['user_id'])
.then(function (data) {
return sendEmail(event["email"], data);
})
.catch(function (err) {
console.log("==ERROR==");
callback(err, err);
})
.finally(function () {});
// delete message
const params = {
QueueUrl: QUEUE_URL,
ReceiptHandle: message.ReceiptHandle,
};
SQS.deleteMessage(params, (err) => callback(err, message));
}
function invokePoller(functionName, message) {
const payload = {
operation: PROCESS_MESSAGE,
message,
};
const params = {
FunctionName: functionName,
InvocationType: 'Event',
Payload: new Buffer(JSON.stringify(payload)),
};
return new Promise((resolve, reject) => {
Lambda.invoke(params, (err) => (err ? reject(err) : resolve()));
});
}
function poll(functionName, callback) {
const params = {
QueueUrl: QUEUE_URL,
MaxNumberOfMessages: 10,
VisibilityTimeout: 10,
};
// batch request messages
SQS.receiveMessage(params, (err, data) => {
if (err) {
return callback(err);
}
// for each message, reinvoke the function
const promises = data.Messages.map((message) => invokePoller(functionName, message));
// complete when all invocations have been made
Promise.all(promises).then(() => {
const result = `Messages received: ${data.Messages.length}`;
callback(null, result);
});
});
}
exports.handler = (event, context, callback) => {
try {
if (event.operation === PROCESS_MESSAGE) {
console.log("Invoked by poller");
process(event.message, callback);
} else {
console.log("invoked by schedule");
poll(context.functionName, callback);
}
} catch (err) {
callback(err);
}
};
can somebody throw me some light to this?
Thanks in advice.
UPDATE
After so much misconception, I've decided to start looking on how the example of polling-SQS works provided by AWS.
There I've found that I lacked some basic SQS permissions, but solved now by adding the right policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": ["*"]
}]
}
This allows Lambda.invoke() to call process().
When the process(message, callback) is called, if I console.log(message);, it seems that there's no message, although the queue is being cleared by the line SQS.deleteMessage(params, (err) => callback(err, message));
What I was trying was to combine my sendMail function that is currently working with a SQS service so I only have to push each message to the queue.
This is a common requirement where AWS SES has its own limitations in sending emails at once. If these limitations are violated, the SES account will sandbox itself. It seems like you have solved the problem using proper access credentials.
This code contains a Python3 Lambda code that can be used to handle a situation like this, where a Lambda polls from SQS using threading, and sends emails using SES, without exceeding the given limitations.
Link to Github Project.
You can also consider using the new feature in SQS, which is capable of invoking lambdas, when a new message is placed within SQS. But, be careful not to exceed the maximum number of lambda functions within the AWS Account region. (See this document)

Resources