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