UNPKG

lemon-core

Version:
272 lines 10.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LambdaHandler = exports.buildReportError = exports.LambdaSubHandler = void 0; /** * `lambda-handler.ts` * - main lambda handler. * * * @author Steve Jung <steve@lemoncloud.io> * @date 2019-11-20 initial version via backbone * * @copyright (C) 2019 LemonCloud Co Ltd. - All Rights Reserved. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars const engine_1 = require("../../engine/"); const test_helper_1 = require("../../common/test-helper"); const NS = engine_1.$U.NS('LMDA', 'green'); // NAMESPACE TO BE PRINTED. /** * */ const $handlerTypes = { alb: 'alb', web: 'web', sns: 'sns', sqs: 'sqs', wss: 'wss', dds: 'dds', cron: 'cron', cognito: 'cognito', 'dynamo-stream': 'dynamo-stream', notification: 'notification', }; class LambdaSubHandler { constructor(lambda, type) { if (!lambda) throw new Error('@lambda (lambda-handler) is required!'); this.lambda = lambda; this.type = type; if (lambda && type) lambda.setHandler(type, this); } } exports.LambdaSubHandler = LambdaSubHandler; /** * build reprot-error function in safe. * * @param isReport flag to report-error via sns * @return the last error message */ const buildReportError = (isReport) => (e, context, event, data) => { return (isReport ? (0, engine_1.doReportError)(e, context, event, data) : Promise.resolve(data)) .then(() => (0, test_helper_1.GETERR)(e)) .catch(test_helper_1.GETERR); }; exports.buildReportError = buildReportError; /** * class: `LambdaHandler` * - general lambda handler so that routes to proper target handlers. */ class LambdaHandler { //* protected constructor. constructor(config) { //* handler map. this._map = {}; this.config = config; } /** * set service lambda handler. * @param type name of type * @param handler handler of service */ setHandler(type, handler) { let key = `${type || ''}`.toLowerCase().trim(); key = key === 'dynamo-stream' ? 'dds' : key; // console.info(`! set-handler[${type}] =`, typeof handler); if (key) this._map[key] = handler; } /** * get service lambda handler. * @param type name of type */ getHandler(type) { let key = `${type || ''}`.toLowerCase().trim(); key = key === 'dynamo-stream' ? 'dds' : key; const handler = this._map[key]; //* must be `LambdaHandlerService`. if (handler && typeof handler == 'object') return handler; return null; } //* Find Service By Event findService(event) { var _a, _b; const headers = (event && event.headers) || {}; (0, engine_1._log)(NS, `> headers =`, engine_1.$U.json(headers)); //* check if AWS SNS Notification Subscription -> notification controller. if (event.requestContext && headers['x-amz-sns-message-type'] && headers['x-amz-sns-message-id'] && headers['x-amz-sns-topic-arn']) { //* via HTTP/HTTPS SNS return 'notification'; } else if (event.requestContext && event.pathParameters !== undefined) { //* via ApiGateway return 'web'; } else if (event.requestContext && event.requestContext.eventType !== undefined) { //* via WEB-SOCKET from ApiGateway return 'wss'; } else if (((_a = event.requestContext) === null || _a === void 0 ? void 0 : _a.elb) && typeof ((_b = event.requestContext.elb) === null || _b === void 0 ? void 0 : _b.targetGroupArn) === 'string') { //* via TargetGroup from ALB(Applicatin/Elastic Load Balancer) return 'alb'; } else { if (event.cron) { //* via CloudWatch's cron. return 'cron'; } else if (event.userPoolId) { //* via cognito event return 'cognito'; } else if (event.Records) { //* decode `Records` to find target. const records = Array.isArray(event.Records) ? event.Records : []; const sns = records.filter((_) => (_.Sns ? true : false)); // via sns event. const sqs = records.filter((_) => _.eventSource == 'aws:sqs'); // via sqs data/ const ddb = records.filter((_) => (_.dynamodb ? true : false)); // via dynamodb if (sns.length) return 'sns'; if (sqs.length) return 'sqs'; if (ddb.length) return 'dds'; } } } /** * decode event to proper handler. * - NOTE! - returns promised results with `async` * * @returns boolean */ handle(event, context) { return __awaiter(this, void 0, void 0, function* () { if (!event) throw new Error('@event is required!'); //* WARN! allows for using callbacks as finish/error-handlers if (context) context.callbackWaitsForEmptyEventLoop = false; //* Check API parameters. const main = (event, context, callback) => { const type = this.findService(event); (0, engine_1._log)(NS, `main(${type})...`); const handler = this._map[type]; if (handler && typeof handler == 'function') { //* low level handler function. return handler(event, context, callback); } else if (handler && typeof handler == 'object') { //* must be `LambdaHandlerService`. const $svc = handler; const $ctx = $svc.packContext ? $svc.packContext(event, context) : this.packContext(event, context); if ($ctx && $ctx instanceof Promise) { return $ctx.then(_ => $svc.handle(event, _)); } else if ($ctx) { return $svc.handle(event, $ctx); } return $svc.handle(event, null); } //* raise error if not found. (0, engine_1._inf)(NS, `WARN! unknown[${type}].event =`, engine_1.$U.json(event)); callback && callback(new Error(`400 UNKNOWN EVENT - service:${type !== null && type !== void 0 ? type : ''}`)); }; //* call promised. const promise = (main, event, context) => new Promise((resolve, reject) => { try { let resolved = false; const R = main(event, context, (error, result) => { error && (0, engine_1._err)(NS, '! err@cb =', error); // !error && _inf(NS, '! res@cb =', result); if (error) reject(error); else if (!resolved) resolve(result); }); if (R !== undefined) { resolved = true; resolve(R); } } catch (e) { return reject(e); } }); //* call main.. (it will return result or promised) return promise(main, event, context) .then(_ => { // if (_ !== undefined) _log(NS, '! res =', $U.json(_)); if (_ !== undefined) (0, engine_1._log)(NS, '! res =', engine_1.$U.S(_, 320, 64, ' .... ')); //* cut result string. // ((context && context.done) || callback)(null, _); // return true; return _; }) .catch(e => { (0, engine_1._err)(NS, '! err =', e); if (!LambdaHandler.REPORT_ERROR) { // ((context && context.done) || callback)(e, null); // return false; throw e; } //* report this error. return (0, engine_1.doReportError)(e, context, event) .catch(_ => _) // safe call w/o error. .then(() => { // ((context && context.done) || callback)(e, null); // return false; throw e; }); }); }); } /** * handle param via protocol-service. * - sub-service could call this method() to bypass request. * * @param param protocol parameters */ handleProtocol(param) { return __awaiter(this, void 0, void 0, function* () { //* if valid API Request, then use $web's function. const $web = this._map['web']; if (!$web || typeof $web != 'object') throw new Error(`500 NO WEB HANDLER - name:web`); return $web.handleProtocol(param); }); } /** * (default) pack the origin context to application context. * - override this function if required! * * @param event origin event * @param $ctx origin context of lambda */ packContext(event, $ctx) { return __awaiter(this, void 0, void 0, function* () { const context = {}; $ctx && (0, engine_1._log)(NS, `! context[${$ctx.functionName || ''}] =`, engine_1.$U.json($ctx)); return context; }); } } exports.LambdaHandler = LambdaHandler; //* shared config. LambdaHandler.REPORT_ERROR = engine_1.$U.env('REPORT_ERROR', '1') == '1'; //# sourceMappingURL=lambda-handler.js.map