@benie/lambda-lib
Version:
Builders and tools for creating AWS Lambda function handlers that provides automation for things such as logging, instrumentation and parameters propagation
148 lines (122 loc) • 5.03 kB
JavaScript
const HandledException = require('./handled-exception');
const middy = require('@middy/core');
const httpJsonBodyParser = require('@middy/http-json-body-parser');
const { captureCorrelationIds, sampleLogging } = require('./middleware');
const DEFAULT_SAMPLE_DEBUG_LOG_RATE = 0.05;
const HTTP_CODE_SUCCESS = 200;
//TODO o wrapper pode ser implementado como um middleware middy
const LambdaEndpointWrapper = (f) => {
return new LambdaEndpoint()
.withHandler(f)
.build();
};
/**
* Handler for HTTP lambda endpoints.
*
* Usage Ex.:
*
* myLambdaModule.myLambdaHandler = new LambdaEndpoint()
* .withHandler(myServiceModule.someBusinessHandler)
* .withStaticParams('myParam1', myParam2)
* // or, alternativelly:
* // .withEventParams(e => [e.body.some, e.queryStringParams.someOther])
* .build();
*/
//TODO deprecar essa Builder Class e usar somente o wrapper
class LambdaEndpoint {
withHandler(handler) {
this.handler = handler;
return this;
}
withEventParams(eventToParamsMapperFunction) {
this.eventToParamsMapperFunction = eventToParamsMapperFunction;
return this;
}
withStaticParams(...params) {
this.params = params;
return this;
}
withEventValidator(f) {
this.validateEvent = f;
return this;
}
build() {
if (this.params && this.eventToParamsMapperFunction) {
throw new Error('Error building lambda handler module: must use either "withParams(...params)" or "withEventParams(mapperFunc)", not both');
}
if(!this.handler) {
throw new Error('Must define an underlying function using "withHandler(<handlerFunction>)" before calling "build()');
}
let res = async (_event, context, callback) => {
let event = Object.assign({}, _event);
if (event.body == null) event.body = {};
if (event.pathParameters == null) event.pathParameters = {};
if (event.queryStringParameters == null) event.queryStringParameters = {};
if (this.validateEvent) this.validateEvent(event);
let params;
if (this.params) {
params = this.params;
} else if (this.eventToParamsMapperFunction) {
params = this.eventToParamsMapperFunction(event);
} else {
params = [event, context, callback];
}
if (!Array.isArray(params)) {
params = [params]; //forces conversion to array
}
try {
let response = await this.handler(...params);
_handleSuccess(response, callback);
}
catch (e) {
_handleError(e, callback);
}
};
const disableSampleLogging = (process.env.DISABLE_SAMPLE_DEBUG_LOG === 'true');
return middy(res)
.use(httpJsonBodyParser())
.use(captureCorrelationIds({ sampleDebugLogRate: DEFAULT_SAMPLE_DEBUG_LOG_RATE }))
.use(sampleLogging({ sampleRate: DEFAULT_SAMPLE_DEBUG_LOG_RATE, forceDisable: disableSampleLogging }));
}
}
LambdaEndpoint.Wrap = LambdaEndpointWrapper;
module.exports = LambdaEndpoint;
/**
* This method handles any business exception by converting it to the proper http response and invoking the proper callback
*
* @param {*} exception The instance of the business exception thrown by the business service lib
* @param {*} callback The callback function used to send the response to the api channel
*/
function _handleError(exception, callback) {
if (exception instanceof HandledException) {
let response = {
statusCode: exception.statusCode || exception.httpStatusCode || 400,
body: exception.message,
};
response.headers = { 'Content-Type': 'text/plain' };
//TODO de acordo com a nova spec do lambda, não é mais necessário chamar callback, podendo somente retornar o resultado.
callback(null, response);
}
else {
//TODO de acordo com a nova spec do lambda, não é mais necessário chamar callback, podendo somente lançar a exception.
callback(exception);
}
}
function _handleSuccess(responseBody, callback) {
let isObject = (responseBody !== null) && (typeof responseBody === 'object');
let response = {
statusCode: HTTP_CODE_SUCCESS,
headers: {
'Access-Control-Allow-Credentials': true // Required for cookies, authorization headers with HTTPS
}
};
if (isObject) {
response.body = JSON.stringify(responseBody);
response.headers['content-type'] = 'application/json';
} else {
response.body = responseBody;
response.headers['content-type'] = 'text/plain';
}
//TODO de acordo com a nova spec do lambda, não é mais necessário chamar callback, podendo somente retornar o resultado.
callback(null, response);
}