@bitblit/ratchet-epsilon-common
Version:
Tiny adapter to simplify building API gateway Lambda APIS
118 lines • 5 kB
JavaScript
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