UNPKG

@bitblit/ratchet-epsilon-common

Version:

Tiny adapter to simplify building API gateway Lambda APIS

118 lines 5 kB
import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet'; import { Logger } from '@bitblit/ratchet-common/logger/logger'; import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet'; import { RestfulApiHttpError } from '@bitblit/ratchet-common/network/restful-api-http-error'; import { ResponseUtil } from './response-util.js'; import { BuiltInFilters } from '../built-in/http/built-in-filters.js'; import { RunHandlerAsFilter } from '../built-in/http/run-handler-as-filter.js'; import { ContextUtil } from '../util/context-util.js'; import { LambdaEventDetector } from '@bitblit/ratchet-aws/lambda/lambda-event-detector'; import Route from 'route-parser'; export class WebHandler { routerConfig; static MAXIMUM_LAMBDA_BODY_SIZE_BYTES = 1024 * 1024 * 5 - 1024 * 100; constructor(routerConfig) { this.routerConfig = routerConfig; RequireRatchet.notNullOrUndefined(routerConfig); } get router() { return this.routerConfig; } extractLabel(evt, _context) { return 'WEB:' + StringRatchet.trimToEmpty(evt.httpMethod).toUpperCase() + ':' + evt.path; } handlesEvent(evt) { return LambdaEventDetector.isValidApiGatewayEvent(evt); } async processEvent(event, context) { if (!this.routerConfig) { throw new Error('Router config not found'); } const asExtended = Object.assign({}, { parsedBody: null, authorization: null, convertedFromV2Event: false }, event); const rval = await this.openApiLambdaHandler(asExtended, context); ContextUtil.addTraceToProxyResult(rval); Logger.updateTracePrefix(null); return rval; } async openApiLambdaHandler(evt, context) { const rm = this.findBestMatchingRoute(evt); const procConfig = rm?.mapping?.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 || []); RunHandlerAsFilter.addRunHandlerAsFilterToList(filterChain, rm); filterChain = filterChain.concat(procConfig.postFilters || []); await BuiltInFilters.combineFilters(fCtx, filterChain); } catch (err) { const wrapper = RestfulApiHttpError.wrapError(err); fCtx.result = ResponseUtil.errorResponse(wrapper); try { await BuiltInFilters.combineFilters(fCtx, procConfig.errorFilters); } catch (convErr) { Logger.error('REALLY BAD - FAILED WHILE PROCESSING ERROR FILTERS : %s', convErr); } } return fCtx.result; } findBestMatchingRoute(event) { let rval = null; const cleanPath = this.cleanPath(event); 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(r.path); const parsed = routeParser.match(cleanPath); if (parsed !== false) { rval = { mapping: r, route: routeParser, parsed: parsed, }; } } return rval; }) .filter((r) => r != null); 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) { 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; while (rval.startsWith('/')) { rval = rval.substring(1); } if (this.routerConfig.config.prefixesToStripBeforeRouteMatch) { this.routerConfig.config.prefixesToStripBeforeRouteMatch.forEach((prefix) => { if (rval.toLowerCase().startsWith(prefix.toLowerCase() + '/')) { rval = rval.substring(prefix.length); } }); } while (rval.startsWith('/')) { rval = rval.substring(1); } rval = '/' + rval; return rval; } } //# sourceMappingURL=web-handler.js.map