@bitblit/epsilon
Version:
Tiny adapter to simplify building API gateway Lambda APIS
146 lines • 6.79 kB
JavaScript
;
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