Serverless authorizer async await not working - async-await

I've been working with Node 6 with my Serverless application, and I decided to migrate to async/await, since version 8.x was released.
Altough, I'm having an issue with the authorizer function. Since I removed the callback parameter and just returning the value, it stopped to work. If I send something to the callback parameter, it keeps working fine, but it's not async/await-like. It's not working even if I throw an exception.
module.exports.handler = async (event, context) => {
if (typeof event.authorizationToken === 'undefined') {
throw new InternalServerError('Unauthorized');
}
const decodedToken = getDecodedToken(event.authorizationToken);
const isTokenValid = await validateToken(decodedToken);
if (!isTokenValid) {
throw new InternalServerError('Unauthorized');
} else {
return generatePolicy(decodedToken);
}
};
Any suggestions of how to proceed?
Thank y'all!

I faced the same problem here. It seems authorizers dont support async/await yet. One solution would be get your entire async/await function and call inside the handler. Something like this:
const auth = async event => {
if (typeof event.authorizationToken === 'undefined') {
throw new InternalServerError('Unauthorized');
}
const decodedToken = getDecodedToken(event.authorizationToken);
const isTokenValid = await validateToken(decodedToken);
if (!isTokenValid) {
throw new InternalServerError('Unauthorized');
} else {
return generatePolicy(decodedToken);
}
}
module.exports.handler = (event, context, cb) => {
auth(event)
.then(result => {
cb(null, result);
})
.catch(err => {
cb(err);
})
};

For folks arriving here in 2020 - now it works as OP described.

Related

Application broken after changing ToPromise() to Last/FirstvalueFrom()

I have this Interceptor which is responsible for the pagination of my application, it has the first part which used to check if the response of http was ok.
try {
//next.handle().pipe(map((data) => ({ data })));
const data = await next.handle().toPromise();
return data.status < 300
? this.mountPagination(data)
: this.getResponseStatus(data, context);
} catch (error) {
return throwError(this.createHttpException(error));
}
}
If it was ok, then it move on and build the pagination.
After the toPromise was deprecated, I wasn't able to change it by the lastvaluefrom
UPDATE
try {
const data = next.handle().pipe(map((data) => ({ data })));
return this.mountPagination(data);
} catch (error) {
return throwError(this.createHttpException(error));
}
}
This is what i'm trying to correct
But the variable data is an observable after that, which I can't get the status from it
How to convert from ToPromise() to First/LastValueFrom:
const data = await next.handle().toPromise();
to
const data = await lastValueFrom(next.handle());
Hope this helps

Why is my lambda taking exactly 6 seconds every time to respond?

I am using NodeJS env with serverless framework.
The service is an endpoint for a contact form submission. Code looks something like this.
I have two async calls, one is writing to dynamoDB and another is sending an Email via SES.
module.exports.blog = async (event, context, callback) => {
const data = JSON.parse(event.body);
const handler = 'AB';
const sesParams = getSesParams(handler, data);
if (typeof data.text !== 'string') {
callback(null, validationErrRes);
return;
}
try {
await logToDB(handler, data);
} catch (dbErr) {
console.error(dbErr);
callback(null, errRes(dbErr, 'Failed to log to DB'));
return;
}
try {
await SES.sendEmail(sesParams).promise();
} catch (emailErr) {
console.error(emailErr);
callback(null, errRes(emailErr, 'Failed to send mail'));
return;
}
callback(null, succsessResponse);
return;
};
The response takes exactly 6sec when the dbput and sendMail takes total of < 300ms.
PS: Running both async calls parallelly does not help much.
Try removing the callback in your function definition and the call to your callback function. Just return the successResponse. You are already an async function so do not need to use a callback. You can also just return error.
module.exports.blog = async (event, context) => {
and
return {
statusCode: 200
}
and
return validationErrRes

Codepipeline lambda action never complete

Well, my lambda function work's well according to the log's, but it never get completed in the codepipeline stage, I have already set permission to role for allow notificate pipeline ("codepipeline:PutJobSuccessResult",
"codepipeline:PutJobFailureResult") and even set maximun time to 20sec but still not working (it actually ends at 800ms).
const axios = require('axios')
const AWS = require('aws-sdk');
const url = 'www.exampleurl.com'
exports.handler = async (event, context) => {
const codepipeline = new AWS.CodePipeline();
const jobId = event["CodePipeline.job"].id;
const stage = event["CodePipeline.job"].data.actionConfiguration.configuration.UserParameters;
const putJobSuccess = function(message) {
var params = {
jobId: jobId
};
codepipeline.putJobSuccessResult(params, function(err, data) {
if (err) {context.fail(err); }
else {context.succeed(message);}
});
};
const putJobFailure = function(message) {
var params = {
jobId: jobId,
failureDetails: {
message: JSON.stringify(message),
type: 'JobFailed',
externalExecutionId: context.invokeid
}
};
codepipeline.putJobFailureResult(params, function(err, data) {
if (err) console.log(err)
context.fail(message);
});
};
try {
await axios.post(url, { content: stage})
putJobSuccess('all fine')
} catch (e) {
putJobFailure(e)
}
};
The root issue
Because nodeJS runs everything async by default, codepipeline.putJobSuccessResult is being run async. The issue seems to be that the Lambda function is finishing it's execution before codepipeline.putJobSuccessResult has a chance to complete.
The solution
Run codepipeline.putJobSuccessResult synchronously so that it is forced to complete before the response is returned to Lambda for the lambdaHandler.
const putJobSuccess = function(id) {
//await sleep(60);
console.log("Telling Codepipeline test passed for job: " + id)
var params = {
jobId: id
};
return codepipeline.putJobSuccessResult(params, function(err, data) {
if(err) {
console.error(err)
} else {
console.log(data)
}
}).promise()
};
exports.lambdaHandler = async (event, context) => {
...
await putJobSuccess( jobId )
return response
};
Whenever I see this issue, most of the time it is due to 'PutJobSuccessResult' never being invoked. The best way to check this is to go to CloudTrail > 'Event History' and look for 'Event name' = 'PutJobSuccessResult' during the time range you expect the Lambda function calling this API. Probably you will not find the 'PutJobSuccessResult', then please have a look at the code again and the Lambda execution logs in CloudWatch.

How can I create subsegments in AWSXRay?

Objective: show errors on AWS X-ray (all errors from lambda).
'use strict';
const AWSXRay = require('aws-xray-sdk-core'),
AWS = AWSXRay.captureAWS(require('aws-sdk')),
env = process.env;
AWS.config.update({
region: env.REGION
});
const dynamodbDocumentClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context, callback) => {
let seg=AWSXRay.getSegment();
try {
const params = {
/*PARAMS HERE*/
};
let res = await dynamodbDocumentClient.scan(params).promise();
throw('ERROR FOR TESTING');
return res;
//callback(null,res);
}
catch(err) {
let subseg=seg.addNewSubsegment('error');
subseg.addMetadata("error", "error", "my_error");
subseg.addAnnotation('errr', 'this is a test');
subseg.addError(err);
subseg.addErrorFlag();
subseg.close();
console.log('==ERROR==',err);
return err;
}
};
When I use AWSXRay.captureAWS the subsegment 'error' doesnt show on X-ray. If I dont use captureAWS the error appear in X-ray correctly.
The issue is likely with how you're structuring your Lambda. First, you shouldn't be using callback in an async function handler, async function handlers should return native promises or errors per the docs.
If you want to use a callback to return the result of your function, which it seems like you do, the handler should be synchronous. That being said, you're throwing your error after the callback, which will not be reflected in X-Ray because the X-Ray segment for Lambda functions closes when the callback is called. If an error is thrown before the callback (e.g. during the DynamoDB call) it should be captured in your error subsegment.
EDIT
After the original post was edited, I was unable to reproduce the error with this code:
const AWSXRay = require('aws-xray-sdk');
const AWS = AWSXRay.captureAWS(require('aws-sdk'));
const ddb = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context) => {
try {
const res = await ddb.scan({TableName: 'scorekeep-game'}).promise();
throw('Test Error');
return result;
} catch (e) {
console.log('caught!');
var sub2 = AWSXRay.getSegment().addNewSubsegment('err');
sub2.addError(e);
sub2.addErrorFlag();
sub2.close();
return e;
}
};

dynamodb scan method returning null

below is the code I'm trying to test by passing "type" property as "all". However, the returned data is null. The role set to this lambda is also given appropriate access to DB. There is data in the table as well.
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({ region: 'us-east-2', apiVersion: '2012-08-10' });
exports.handler = async (event, context, callback) => {
// TODO implement
const type = event.type;
if(type === "all"){
const params = {
TableName: 'compare-yourself'
};
dynamodb.scan(params, function(err, data){
if(err){
console.log(err);
callback(err);
} else {
console.log(data);
console.log(type);
callback(null, data);
}
});
} else if(type === "single") {
console.log(type);
callback(null, "Just my Data");
} else {
callback(null, "Hello from Lambda!");
}
};
I added a promise resolve rejection against the scan function and it resolved with a null always. When I removed the same, it worked fine.
This question is old, but I recognize the code. It's from the Udemy course on AWS by Maximilian Schwarzmüller.
If you come here with the same question, to implement the DynamoDB scan function in that course today, with newer versions of nodejs, use a promise and place the dynamodb.scan in a try catch. This code is in the Q&A section of that course under the title "I'm not able to get data".
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({ region: 'us-east-2', apiVersion: '2012-08-10' });
exports.handler = async (event, context, callback) => {
const type = event.type;
if (type === 'all') {
const params = {
TableName: 'compare-yourself'
}
try {
const data = await dynamodb.scan(params).promise()
callback(null, data)
} catch(err) {
callback(err);
}
} else if (type === 'single') {
callback(null, 'Just my data');
} else {
callback(null, 'Hello from Lambda');
}
};

Resources