UNPKG

@bitblit/epsilon

Version:

Tiny adapter to simplify building API gateway Lambda APIS

146 lines 6.79 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()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebHandler = void 0; const common_1 = require("@bitblit/ratchet/common"); const route_parser_1 = __importDefault(require("route-parser")); const response_util_1 = require("./response-util"); const built_in_filters_1 = require("../built-in/http/built-in-filters"); const run_handler_as_filter_1 = require("../built-in/http/run-handler-as-filter"); const context_util_1 = require("../util/context-util"); const aws_1 = require("@bitblit/ratchet/aws"); /** * This class functions as the adapter from a default lambda function to the handlers exposed via Epsilon */ class WebHandler { constructor(routerConfig) { this.routerConfig = routerConfig; common_1.RequireRatchet.notNullOrUndefined(routerConfig); } get router() { return this.routerConfig; } extractLabel(evt, context) { return 'WEB:' + common_1.StringRatchet.trimToEmpty(evt.httpMethod).toUpperCase() + ':' + evt.path; } handlesEvent(evt) { return aws_1.LambdaEventDetector.isValidApiGatewayEvent(evt); } processEvent(event, context) { return __awaiter(this, void 0, void 0, function* () { if (!this.routerConfig) { throw new Error('Router config not found'); } const asExtended = Object.assign({}, { parsedBody: null, authorization: null, convertedFromV2Event: false }, event); const rval = yield this.openApiLambdaHandler(asExtended, context); context_util_1.ContextUtil.addTraceToProxyResult(rval); common_1.Logger.updateTracePrefix(null); // Just in case it was set return rval; }); } openApiLambdaHandler(evt, context) { var _a; return __awaiter(this, void 0, void 0, function* () { const rm = this.findBestMatchingRoute(evt); const procConfig = ((_a = rm === null || rm === void 0 ? void 0 : rm.mapping) === null || _a === void 0 ? void 0 : _a.metaProcessingConfig) ? rm.mapping.metaProcessingConfig : this.routerConfig.config.defaultMetaHandling; const fCtx = { event: evt, context: context, result: null, rawResult: null, routeAndParse: rm, modelValidator: this.routerConfig.config.overrideModelValidator || this.routerConfig.openApiModelValidator, authenticators: this.routerConfig.config.authorizers, }; try { let filterChain = Object.assign([], procConfig.preFilters || []); run_handler_as_filter_1.RunHandlerAsFilter.addRunHandlerAsFilterToList(filterChain, rm); filterChain = filterChain.concat(procConfig.postFilters || []); yield built_in_filters_1.BuiltInFilters.combineFilters(fCtx, filterChain); } catch (err) { // Convert to an epsilon error const wrapper = common_1.RestfulApiHttpError.wrapError(err); fCtx.result = response_util_1.ResponseUtil.errorResponse(wrapper); try { yield built_in_filters_1.BuiltInFilters.combineFilters(fCtx, procConfig.errorFilters); } catch (convErr) { common_1.Logger.error('REALLY BAD - FAILED WHILE PROCESSING ERROR FILTERS : %s', convErr); } } return fCtx.result; }); } findBestMatchingRoute(event) { let rval = null; // See: https://www.npmjs.com/package/route-parser const cleanPath = this.cleanPath(event); // Filter routes to only matches const methodLower = event.httpMethod.toLowerCase(); const matchRoutes = this.routerConfig.routes .map((r) => { let rval = null; if (r.method && r.method.toLowerCase() === methodLower) { const routeParser = new route_parser_1.default(r.path); const parsed = routeParser.match(cleanPath); if (parsed !== false) { rval = { mapping: r, route: routeParser, parsed: parsed, }; } } return rval; }) .filter((r) => r != null); // Pick the 'best' match matchRoutes.sort((a, b) => { return Object.keys(a.parsed).length - Object.keys(b.parsed).length; }); rval = matchRoutes && matchRoutes.length > 0 ? matchRoutes[0] : null; if (!rval) { common_1.Logger.debug('Failed to find handler for %s (cleaned path was %s, strip prefixes were %j)', event.path, cleanPath, this.routerConfig.config.prefixesToStripBeforeRouteMatch); } return rval; } cleanPath(event) { let rval = event.path; // First, strip any leading / while (rval.startsWith('/')) { rval = rval.substring(1); } // If there are any listed prefixes, strip them if (this.routerConfig.config.prefixesToStripBeforeRouteMatch) { this.routerConfig.config.prefixesToStripBeforeRouteMatch.forEach((prefix) => { if (rval.toLowerCase().startsWith(prefix.toLowerCase() + '/')) { rval = rval.substring(prefix.length); } }); } // Strip any more leading / while (rval.startsWith('/')) { rval = rval.substring(1); } // Finally, put back exactly 1 leading / to match what comes out of open api rval = '/' + rval; return rval; } } exports.WebHandler = WebHandler; WebHandler.MAXIMUM_LAMBDA_BODY_SIZE_BYTES = 1024 * 1024 * 5 - 1024 * 100; // 5Mb - 100k buffer //# sourceMappingURL=web-handler.js.map