UNPKG

@bitblit/epsilon

Version:

Tiny adapter to simplify building API gateway Lambda APIS

129 lines 7.63 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ApolloFilter = void 0; const common_1 = require("@bitblit/ratchet/common"); const request_timeout_error_1 = require("../../http/error/request-timeout-error"); const server_1 = require("@apollo/server"); const context_util_1 = require("../../util/context-util"); const apollo_util_1 = require("./apollo-util"); const epsilon_apollo_cors_method_1 = require("./epsilon-apollo-cors-method"); const built_in_filters_1 = require("../../built-in/http/built-in-filters"); class ApolloFilter { static handlePathWithApollo(fCtx, apolloPathRegex, apolloServer, options) { var _a; return __awaiter(this, void 0, void 0, function* () { let rval = false; if (((_a = fCtx.event) === null || _a === void 0 ? void 0 : _a.path) && apolloPathRegex && apolloPathRegex.test(fCtx.event.path)) { fCtx.result = yield ApolloFilter.processApolloRequest(fCtx.event, fCtx.context, apolloServer, options); if (options === null || options === void 0 ? void 0 : options.corsMethod) { switch (options.corsMethod) { case epsilon_apollo_cors_method_1.EpsilonApolloCorsMethod.All: yield built_in_filters_1.BuiltInFilters.addAllowEverythingCORSHeaders(fCtx); break; case epsilon_apollo_cors_method_1.EpsilonApolloCorsMethod.Reflective: yield built_in_filters_1.BuiltInFilters.addAllowReflectionCORSHeaders(fCtx); break; default: // Do nothing } } } else { // Not handled by apollo rval = true; } return rval; }); } static processApolloRequest(event, context, apolloServer, options) { var _a, _b, _c; return __awaiter(this, void 0, void 0, function* () { common_1.Logger.silly('Processing event with apollo: %j', event); let rval = null; common_1.RequireRatchet.notNullOrUndefined(apolloServer, 'apolloServer'); apolloServer.assertStarted('Cannot process with apollo - instance not started'); const headerMap = new server_1.HeaderMap(); for (const headersKey in event.headers) { headerMap.set(headersKey, event.headers[headersKey]); } const eventMethod = common_1.StringRatchet.trimToEmpty(event.httpMethod).toUpperCase(); let body = null; if (common_1.StringRatchet.trimToNull(event.body)) { const bodyString = event.isBase64Encoded ? common_1.Base64Ratchet.base64StringToString(event.body) : event.body; body = JSON.parse(bodyString); } const aRequest = { method: eventMethod, headers: headerMap, search: eventMethod === 'GET' && (event === null || event === void 0 ? void 0 : event.queryStringParameters) ? Object.keys(event.queryStringParameters) .map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(event.queryStringParameters[k])) .join('&') : null, body: body, }; // We do this because fully timing out on Lambda is never a good thing const timeoutMS = (_a = options === null || options === void 0 ? void 0 : options.timeoutMS) !== null && _a !== void 0 ? _a : context.getRemainingTimeInMillis() - 500; //const defaultContextFn: ContextFunction<[EpsilonLambdaApolloContextFunctionArgument], any> = async () => ({}); const contextFn = (_b = options === null || options === void 0 ? void 0 : options.context) !== null && _b !== void 0 ? _b : apollo_util_1.ApolloUtil.emptyContext; const apolloPromise = apolloServer.executeHTTPGraphQLRequest({ httpGraphQLRequest: aRequest, context: () => contextFn({ lambdaContext: context, lambdaEvent: event }), }); let result = null; if (timeoutMS) { result = yield common_1.PromiseRatchet.timeout(apolloPromise, 'Apollo timed out after ' + timeoutMS + ' ms.', timeoutMS); } else { common_1.Logger.warn('No timeout set even after defaulting for Apollo'); result = yield apolloPromise; } if (common_1.TimeoutToken.isTimeoutToken(result)) { result.writeToLog(); throw new request_timeout_error_1.RequestTimeoutError('Timed out'); } // If we reach here we didn't time out const httpGraphQLResponse = result; // TODO: Use typeguard here instead const outHeaders = {}; for (const [headersKey, headersValue] of httpGraphQLResponse.headers.entries()) { outHeaders[headersKey] = headersValue; } if (httpGraphQLResponse.body.kind === 'chunked') { // This is legal according to https://www.apollographql.com/docs/apollo-server/integrations/building-integrations/ throw new common_1.RestfulApiHttpError('Apollo returned chunked result').withHttpStatusCode(500).withRequestId(context_util_1.ContextUtil.currentRequestId()); } const bodyAsString = common_1.StringRatchet.trimToEmpty((_c = httpGraphQLResponse === null || httpGraphQLResponse === void 0 ? void 0 : httpGraphQLResponse.body) === null || _c === void 0 ? void 0 : _c.string); rval = { body: common_1.Base64Ratchet.generateBase64VersionOfString(bodyAsString), headers: outHeaders, multiValueHeaders: {}, isBase64Encoded: true, statusCode: httpGraphQLResponse.status || 200, }; // Finally, a double check to set the content type correctly if the browser page was shown // Since otherwise Apollo defaults it to application/json for some reason if (eventMethod === 'GET' && rval.headers['content-type'] !== 'text/html' && bodyAsString.indexOf('<!DOCTYPE html>') >= 0) { common_1.Logger.info('Forcing content type to html for the sandbox page'); rval.headers = rval.headers || {}; rval.headers['content-type'] = 'text/html'; } return rval; }); } static addApolloFilterToList(filters, apolloPathRegex, apolloServer, options) { if (filters) { filters.push((fCtx) => ApolloFilter.handlePathWithApollo(fCtx, apolloPathRegex, apolloServer, options)); } } } exports.ApolloFilter = ApolloFilter; //# sourceMappingURL=apollo-filter.js.map