UNPKG

@bitblit/epsilon

Version:

Tiny adapter to simplify building API gateway Lambda APIS

266 lines 14.9 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 __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var logger_1 = require("@bitblit/ratchet/dist/common/logger"); var http = require("http"); var web_handler_1 = require("./http/web-handler"); var promise_ratchet_1 = require("@bitblit/ratchet/dist/common/promise-ratchet"); var string_ratchet_1 = require("@bitblit/ratchet/dist/common/string-ratchet"); var moment = require("moment-timezone"); var qs = require("querystring"); var simple_role_route_auth_1 = require("./http/auth/simple-role-route-auth"); var router_util_1 = require("./http/route/router-util"); var fs = require("fs"); var path = require("path"); var built_in_handlers_1 = require("./http/route/built-in-handlers"); var epsilon_constants_1 = require("./epsilon-constants"); var error_ratchet_1 = require("@bitblit/ratchet/dist/common/error-ratchet"); var number_ratchet_1 = require("@bitblit/ratchet/dist/common/number-ratchet"); var event_util_1 = require("./http/event-util"); /** * A simplistic server for testing your lambdas locally */ var LocalServer = /** @class */ (function () { function LocalServer(routerConfig, port) { if (port === void 0) { port = 8888; } this.routerConfig = routerConfig; this.port = port; this.aborted = false; this.webHandler = new web_handler_1.WebHandler(routerConfig); } LocalServer.prototype.runServer = function () { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { logger_1.Logger.info('Starting Epsilon server on port %d', this.port); this.server = http.createServer(this.requestHandler.bind(this)).listen(this.port); logger_1.Logger.info('Epsilon server is listening'); // Also listen for SIGINT process.on('SIGINT', function () { logger_1.Logger.info('Caught SIGINT - shutting down test server...'); _this.aborted = true; }); return [2 /*return*/, this.checkFinished()]; }); }); }; LocalServer.prototype.checkFinished = function () { return __awaiter(this, void 0, void 0, function () { var wait; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.aborted) return [3 /*break*/, 1]; return [2 /*return*/, true]; case 1: return [4 /*yield*/, promise_ratchet_1.PromiseRatchet.wait(1000)]; case 2: wait = _a.sent(); return [2 /*return*/, this.checkFinished()]; } }); }); }; LocalServer.prototype.requestHandler = function (request, response) { return __awaiter(this, void 0, void 0, function () { var evt, context, logEventLevel, result, written; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.messageToApiGatewayEvent(request)]; case 1: evt = _a.sent(); context = { awsRequestId: 'LOCAL-' + string_ratchet_1.StringRatchet.createType4Guid() }; logEventLevel = event_util_1.EventUtil.eventIsAGraphQLIntrospection(evt) ? 'silly' : 'info'; logger_1.Logger.logByLevel(logEventLevel, 'Processing event: %j', evt); if (!(evt.path == '/epsilon-poison-pill')) return [3 /*break*/, 2]; this.aborted = true; return [2 /*return*/, true]; case 2: return [4 /*yield*/, this.webHandler.lambdaHandler(evt, context)]; case 3: result = _a.sent(); return [4 /*yield*/, this.writeProxyResultToServerResponse(result, response)]; case 4: written = _a.sent(); return [2 /*return*/, written]; } }); }); }; LocalServer.prototype.bodyAsBase64String = function (request) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, new Promise(function (res, rej) { var body = []; request.on('data', function (chunk) { body.push(chunk); }); request.on('end', function () { var rval = Buffer.concat(body).toString('base64'); res(rval); }); })]; }); }); }; LocalServer.prototype.messageToApiGatewayEvent = function (request) { return __awaiter(this, void 0, void 0, function () { var bodyString, stageIdx, stage, path, reqTime, formattedTime, queryIdx, queryStringParams, headers, rval; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.bodyAsBase64String(request)]; case 1: bodyString = _a.sent(); stageIdx = request.url.indexOf('/', 1); stage = request.url.substring(1, stageIdx); path = request.url.substring(stageIdx + 1); reqTime = new Date().getTime(); formattedTime = moment.tz(reqTime, 'UTC').format('DD/MMM/YYYY:hh:mm:ss ZZ'); queryIdx = request.url.indexOf('?'); queryStringParams = queryIdx > -1 ? qs.parse(request.url.substring(queryIdx + 1)) : {}; headers = Object.assign({}, request.headers); headers['X-Forwarded-Proto'] = 'http'; // This server is always unencrypted rval = { body: bodyString, multiValueHeaders: {}, multiValueQueryStringParameters: {}, resource: '/{proxy+}', path: request.url, httpMethod: request.method.toLowerCase(), isBase64Encoded: true, queryStringParameters: queryStringParams, pathParameters: { proxy: path }, stageVariables: { baz: 'qux' }, headers: headers, requestContext: { accountId: '123456789012', resourceId: '123456', stage: stage, requestId: string_ratchet_1.StringRatchet.createType4Guid(), requestTime: formattedTime, requestTimeEpoch: reqTime, identity: null, /* identity: { apiKey: null, cognitoIdentityPoolId: null, accountId: null, cognitoIdentityId: null, caller: null, accessKey: null, sourceIp: '127.0.0.1', cognitoAuthenticationType: null, cognitoAuthenticationProvider: null, userArn: null, userAgent: 'Custom User Agent String', user: null }, */ path: request.url, domainName: request.headers['host'], resourcePath: '/{proxy+}', httpMethod: request.method.toLowerCase(), apiId: '1234567890', protocol: 'HTTP/1.1' } }; return [2 /*return*/, rval]; } }); }); }; LocalServer.prototype.writeProxyResultToServerResponse = function (proxyResult, response) { return __awaiter(this, void 0, void 0, function () { var isGraphQLSchemaResponse, toWrite; return __generator(this, function (_a) { isGraphQLSchemaResponse = !!proxyResult && !!proxyResult.body && proxyResult.body.indexOf('{"data":{"__schema"') > -1; if (!isGraphQLSchemaResponse) { logger_1.Logger.debug('Result: %j', proxyResult); } response.statusCode = proxyResult.statusCode; if (proxyResult.headers) { Object.keys(proxyResult.headers).forEach(function (hk) { response.setHeader(hk, String(proxyResult.headers[hk])); }); } toWrite = proxyResult.isBase64Encoded ? Buffer.from(proxyResult.body, 'base64') : Buffer.from(proxyResult.body); response.end(toWrite); return [2 /*return*/, !!proxyResult.body]; }); }); }; return LocalServer; }()); exports.LocalServer = LocalServer; // Functions below here are for using as samples function createSampleRouterConfig() { var yamlString = loadSampleOpenApiYaml(); var authorizers = new Map(); var handlers = new Map(); var simpleRouteAuth = new simple_role_route_auth_1.SimpleRoleRouteAuth(['USER'], []); authorizers.set('SampleAuthorizer', function (token, event, route) { return simpleRouteAuth.handler(token, event, route); }); handlers.set('get /', function (event, context) { return built_in_handlers_1.BuiltInHandlers.sample(event, null, context); }); handlers.set('get /meta/server', function (event) { return built_in_handlers_1.BuiltInHandlers.sample(event); }); handlers.set('get /meta/user', function (event) { return built_in_handlers_1.BuiltInHandlers.sample(event); }); handlers.set('get /meta/item/{itemId}', function (event) { return built_in_handlers_1.BuiltInHandlers.sample(event); }); handlers.set('post /secure/access-token', function (event) { return built_in_handlers_1.BuiltInHandlers.sample(event); }); handlers.set('get /multi/fixed', function (event) { return built_in_handlers_1.BuiltInHandlers.sample(event, 'fixed'); }); handlers.set('get /multi/{v}', function (event) { return built_in_handlers_1.BuiltInHandlers.sample(event, 'variable'); }); handlers.set('get /err/{code}', function (event) { var err = error_ratchet_1.ErrorRatchet.fErr('Fake Err : %j', event); err['statusCode'] = number_ratchet_1.NumberRatchet.safeNumber(event.pathParameters['code']); throw err; }); var cfg = router_util_1.RouterUtil.openApiYamlToRouterConfig(yamlString, handlers, authorizers); cfg.corsAllowedHeaders = epsilon_constants_1.EpsilonConstants.CORS_MATCH_REQUEST_FLAG; cfg.corsAllowedOrigins = epsilon_constants_1.EpsilonConstants.CORS_MATCH_REQUEST_FLAG; cfg.corsAllowedMethods = epsilon_constants_1.EpsilonConstants.CORS_MATCH_REQUEST_FLAG; cfg.defaultErrorMessage = 'Internal Server Error'; return cfg; } exports.createSampleRouterConfig = createSampleRouterConfig; function loadSampleOpenApiYaml() { var yamlString = fs.readFileSync(path.join(__dirname, 'static', 'sample-open-api-doc.yaml')).toString(); return yamlString; } exports.loadSampleOpenApiYaml = loadSampleOpenApiYaml; //# sourceMappingURL=local-server.js.map