AWS SES send email lambda not sending every time - amazon-web-services

I want to send emails using the ses from aws from lambda. The problem is that the email is only sent some times using the same code. We don't get errors.
Here's the code:
const AWS = require('aws-sdk');
var ses = new AWS.SES();
exports.handler = async (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
await new Promise((resolve, reject) => {
var params = {
Destination: {
ToAddresses: [myEmail]
},
Message: {
Body: {
Text: { Data: "Test"
}
},
Subject: { Data: "Test Email"
}
},
Source: "sourceMail"
};
ses.sendEmail(params, function (err, data) {
if (err) {
console.log(err);
context.fail(err);
} else {
console.log(data);
context.succeed(event);
}
callback(null, {err: err, data: data});
});
});
}

I would be careful with using callbackWaitsForEmptyEventLoop as it can lead to unexpected results (If this is false, any outstanding events continue to run during the next invocation.).
Can you try using this simplified version:
const AWS = require('aws-sdk');
var ses = new AWS.SES();
exports.handler = async (event, context, callback) => {
const params = {
Destination: {
ToAddresses: [myEmail],
},
Message: {
Body: {
Text: { Data: 'Test' },
},
Subject: { Data: 'Test Email' },
},
Source: 'sourceMail',
};
await ses.sendEmail(params).promise();
return event;
};

Related

AWS Lambda With SuperAgent And Callback (Sync Callback) always return null

I am currently working on an authentication pipeline that has a similar process to OAuth.
If authentication is successful in the third-party authentication system, the value will be transferred to Success Direct url, which I want to communicate with my server to configure to proceed with self-authentication.
In this situation, I organized the api of the success redirect url into lambda and used Lambda's callback object to process responses and requests in a chain.
But the code below always returned the null value, and I have no idea why.
// index.js
const Auth = require('AuthRequest')
exports.handler = async function (event, context, callback) {
if (event.routeKey == 'POST /authentication/success') {
var body = Auth.parse_query_to_json(Auth.decode_base64(event.body));
if (body.resultCode == '0000') { // success
await Auth.get_auth_info(body, callback);
}
else { // failure
callback(null, {
statusCode: 200,
body: 'failure1'
});
}
}
else if (event.routeKey == 'POST /authentication/failure') {
callback(null, {
statusCode: 200,
body: 'failure2'
});
}
};
// authRequest.js
const superagent = require('superagent')
var get_auth_info = async function (body, callback) {
await superagent
.post(url)
.send({
...
})
.set('Content-Type', 'application/json;charset=utf-8')
.end(async function(err, res) {
if(err) {
callback({
statusCode: 200,
body: 'failure3'
});
}
else {
callback(null, res)
}
});
};
var create_user_auth = async function (req, callback) {
await superagent
.post('my-auth-api-uri')
.send({
})
.set('Content-Type', 'application/json;charset=utf-8')
.end(async function(err, res) {
if(err) {
callback({
statusCode: 200,
body: 'failure4'
});
}
else {
if(res.status == 200) {
callback(null, res)
}
else {
callback({
statusCode: 200,
body: 'failure5'
});
}
}
});
};
......
And this lambda funciton is triggered by a API Gateway and uses the event object of Payload 2.0 version using http api.
Is there anyone who knows the answer to this question?
If you want sync handler, then you should not using async function. Docs explain that it should be:
exports.handler = function(event, context, callback) {
Thanks to #Marcin, my code worked perfectly!
// index.js
const Auth = require('AuthRequest')
exports.handler = function (event, context, callback) {
if (event.routeKey == 'POST /authentication/success') {
var body = Auth.parse_query_to_json(Auth.decode_base64(event.body));
if (body.resultCode == '0000') { // success
Auth.get_auth_info(body, callback);
}
else { // failure
callback(null, {
statusCode: 200,
body: 'failure1'
});
}
}
else if (event.routeKey == 'POST /authentication/failure') {
callback(null, {
statusCode: 200,
body: 'failure2'
});
}
};
// authRequest.js
const superagent = require('superagent')
var get_auth_info = function (body, callback) {
superagent
.post(url)
.send({
...
})
.set('Content-Type', 'application/json;charset=utf-8')
.end(function(err, res) {
if(err) {
callback({
statusCode: 200,
body: 'failure3'
});
}
else {
callback(null, res)
}
});
};
var create_user_auth = function (req, callback) {
superagent
.post('my-auth-api-uri')
.send({
})
.set('Content-Type', 'application/json;charset=utf-8')
.end(function(err, res) {
if(err) {
callback({
statusCode: 200,
body: 'failure4'
});
}
else {
if(res.status == 200) {
callback(null, res)
}
else {
callback({
statusCode: 200,
body: 'failure5'
});
}
}
});
};
......

Running from AWS Lambda function: can see CloudWatch logs, except in axios's continuation

Running the code below in AWS Lambda, I can see in CloudWatch the first part (Sending out to: ${url}), but not the part in axios's .then:
console.log(`Sending out to: ${url}`);
axios.post(url, 'Hi from Lambda!')
.then((response) => {
console.log("Axios succeeded:", response);
}, (error) => {
console.log("Axios failed:", error);
});
Am I missing something? why isn't the response/error visible in CloudWatch?
(I know that axios is working because when I'm using an incorrect address in url, I'm getting e.g. an ECONNREFUSED error)
Edit - full code:
const axios = require('axios');
exports.handler = async (event, connection, publish) => {
console.log(`The event:`, event);
console.log(`Connection:`, connection);
var domainName = event.requestContext.domainName;
var stage = event.requestContext.stage;
var connectionId = event.requestContext.connectionId;
var url = `https://${domainName}/${stage}/#connections/${connectionId}`;
console.log(`Sending out to: ${url}`);
axios.post(url, 'Hi from Lambda!')
.then((response) => {
console.log("Axios succeeded:", response);
}, (error) => {
console.log("Axios failed:", error);
});
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
Your function finished right after Axios request is called. This means the function does not wait until the request complete.
You write the lambda as an async function, then just use await to wait for the Axios request.
const axios = require('axios');
exports.handler = async (event, context) => { // event and context
console.log(`The event:`, event);
console.log(`Connection:`, context);
var domainName = event.requestContext.domainName;
var stage = event.requestContext.stage;
var connectionId = event.requestContext.connectionId;
var url = `https://${domainName}/${stage}/#connections/${connectionId}`;
console.log(`Sending out to: ${url}`);
const response = await axios.post(url, 'Hi from Lambda!') // wait until request response
.catch(error => { // If you don't care about the error, just print it out an continue
console.log("Axios failed:", error);
});
console.log("Axios succeeded:", response);
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};

Lambda function specified as a Destination is not being triggered

I have a lambda function that verifies user credentials. Upon success it should call another lambda function, as a destination, that generates a token. When I test the first function, it is successful but it does not call the destination which is the other lambda function. It only gives me the success message from the first function.
Function one
exports.handler = function (event, context) {
var id = event.email;
var params = {
TableName: "User",
KeyConditionExpression: "#email = :email",
ExpressionAttributeNames:{
"#email": "email",
},
ExpressionAttributeValues: {
":email": {S: event.email},
}
};
if (id && id !== '') {
dynamo.query(params, function (err, data, callback) {
if (err) {
context.done(err);
}
else {
var user = data.Items[0];
if (user) {
var encryptedParams = {
CiphertextBlob: Buffer.from(user.password.B),
};
kms.decrypt(encryptedParams, function (err, decrypteddata) {
if (err) {
console.log(err, err.stack);
context.done(err);
}
else {
if(event.password == decrypteddata.Plaintext.toString()) {
console.log("User authenticated");
}
}
});
}
}
});
}
else {
return {
statusCode: 400,
body: "No email provided."
}
}
};
Function two
exports.handler = async (event) => {
var expires = moment().add('days', 7).valueOf();
var token = jwt.encode({
iss: event.email,
exp: expires
}, app.get('jwtTokenSecret'));
const response = {
token: token,
express: expires,
statusCode: 200
};
console.log("token granted");
return response;
};
Your code doesn't seem to be indicating when a successful execution has been completed. Destinations needs the "OnSuccess" indicator to determine which destination to trigger. This is possibly why the second function is not being executed.
See: Lambda Destinations: What We Learned the Hard Way - CloudProse - Trek10 Blog

AWS cognito users list : lambda

I am working on one node application which is using AWS. now i want to get all cognito users but as per doc it returns first 60 users but i want all users. can you assist me with this? In doc, they mentioned that pass PaginationToken (string) . but i don't know what to pass in it.
Here what i have done so far :
exports.handler = (event, context, callback) => {
const requestBody = JSON.parse(event.body);
var params = {
"UserPoolId": "****************",
"Limit": 60,
"PaginationToken" : (what to pass here????),
}
const cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
if (err) {
callback(null, { headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }, body: JSON.stringify({ statusCode: 405, data: err }) });
} else {
console.log(data);
let userdata = [];
for(let i=0; i<data.Users.length;i++){
// console.log(data.Users[i].Attributes);
userdata.push(getAttributes(data.Users[i].Attributes));
}
callback(null, { headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }, body: JSON.stringify({ statusCode: 200, data: userdata }) });
}
});
};
function getAttributes(attributes){
let jsonObj = {};
attributes.forEach((obj) => {
jsonObj[obj.Name] = obj.Value;
});
return jsonObj;
}
In your response you should see a property called PaginationToken. If you make the same call but include this value in your params you will receive the next 60 users. Here's the concept:
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
// data.Users is the first 60 users
params.PaginationToken = data.PaginationToken;
cognitoidentityserviceprovider.listUsers(params, (err, data) => {
// data.Users is the next 60 users
});
});
You might want to consider switching to promises and async/await if your environment supports it. That would make this code easier to read and write.
const data = await cognitoidentityserviceprovider.listUsers(params).promise();
params.PaginationToken = data.PaginationToken;
const data2 = await cognitoidentityserviceprovider.listUsers(params).promise();

AWS Lambda - unable to convert SDK call to promise

I have a Lambda which looks like so:
module.exports.handler = (event, context, callback) => {
AWS.config.setPromisesDependency(null);
const uploadPromise = s3.upload(params).promise();
uploadPromise.then((data) => {
const response = {
...
};
return response;
}).catch((error) => {
console.log(error);
});
};
Calling it from Postman results in server error in Postman. CloudWatch logs have no further info.
Doing:
s3.upload(params, (error, data) => {
if (error) {
console.error("error occurred storing to s3: ", error);
return;
}
const response = {
...
};
return response;
});
does not result in a server error.
I am trying to follow the information from AWS that can be found here:
https://aws.amazon.com/blogs/developer/support-for-promises-in-the-sdk/
Postman is able to upload to Lambda by doing the following with async/await and try/catch:
exports.handler = async function(event, context) {
const s3 = new AWS.S3();
const encodedImage = util.inspect(event.body);
const decodedImage = Buffer.from(encodedImage, "base64");
const filePath = "test.png";
const params = {
Body: decodedImage,
Bucket: "my bucket",
Key: filePath,
ACL: "public-read",
ContentType: "mime/png"
};
try {
const result = await s3.upload(params).promise();
const response = {
statusCode: 200,
headers: {
my_header: "my_value"
},
body: JSON.stringify(result),
isBase64Encoded: false
};
return response;
} catch (error) {
console.log('error')
}
};