@bitblit/ratchet-epsilon-common
Version:
Tiny adapter to simplify building API gateway Lambda APIS
102 lines • 5.07 kB
JavaScript
import { RequireRatchet } from '@bitblit/ratchet-common/lang/require-ratchet';
import { Logger } from '@bitblit/ratchet-common/logger/logger';
import { PromiseRatchet } from '@bitblit/ratchet-common/lang/promise-ratchet';
import { StringRatchet } from '@bitblit/ratchet-common/lang/string-ratchet';
import { TimeoutToken } from '@bitblit/ratchet-common/lang/timeout-token';
import { RestfulApiHttpError } from '@bitblit/ratchet-common/network/restful-api-http-error';
import { Base64Ratchet } from '@bitblit/ratchet-common/lang/base64-ratchet';
import { RequestTimeoutError } from '../../http/error/request-timeout-error.js';
import { HeaderMap } from '@apollo/server';
import { ContextUtil } from '../../util/context-util.js';
import { ApolloUtil } from './apollo/apollo-util.js';
import { BuiltInFilters } from './built-in-filters.js';
export class ApolloFilter {
static async handlePathWithApollo(fCtx, apolloPathRegex, apolloServer, options) {
let rval = false;
if (fCtx.event?.path && apolloPathRegex && apolloPathRegex.test(fCtx.event.path)) {
fCtx.result = await ApolloFilter.processApolloRequest(fCtx.event, fCtx.context, apolloServer, options);
if (options?.corsMethod) {
await BuiltInFilters.addCorsHeadersDynamically(fCtx, options.corsMethod);
}
}
else {
rval = true;
}
return rval;
}
static async processApolloRequest(event, context, apolloServer, options) {
Logger.silly('Processing event with apollo: %j', event);
let rval = null;
RequireRatchet.notNullOrUndefined(apolloServer, 'apolloServer');
apolloServer.assertStarted('Cannot process with apollo - instance not started');
const headerMap = new HeaderMap();
for (const headersKey in event.headers) {
headerMap.set(headersKey, event.headers[headersKey]);
}
const eventMethod = StringRatchet.trimToEmpty(event.httpMethod).toUpperCase();
let body = null;
if (StringRatchet.trimToNull(event.body)) {
const bodyString = event.isBase64Encoded ? Base64Ratchet.base64StringToString(event.body) : event.body;
body = JSON.parse(bodyString);
}
const aRequest = {
method: eventMethod,
headers: headerMap,
search: eventMethod === 'GET' && event?.queryStringParameters
? Object.keys(event.queryStringParameters)
.map((k) => encodeURIComponent(k) + '=' + encodeURIComponent(event.queryStringParameters[k]))
.join('&')
: null,
body: body,
};
const timeoutMS = options?.timeoutMS ?? context.getRemainingTimeInMillis() - 500;
const contextFn = options?.context ?? ApolloUtil.emptyContext;
const apolloPromise = apolloServer.executeHTTPGraphQLRequest({
httpGraphQLRequest: aRequest,
context: () => contextFn({ lambdaContext: context, lambdaEvent: event }),
});
let result = null;
if (timeoutMS) {
result = await PromiseRatchet.timeout(apolloPromise, 'Apollo timed out after ' + timeoutMS + ' ms.', timeoutMS);
}
else {
Logger.warn('No timeout set even after defaulting for Apollo');
result = await apolloPromise;
}
if (TimeoutToken.isTimeoutToken(result)) {
result.writeToLog();
throw new RequestTimeoutError('Timed out');
}
const httpGraphQLResponse = result;
const outHeaders = {};
for (const [headersKey, headersValue] of httpGraphQLResponse.headers.entries()) {
outHeaders[headersKey] = headersValue;
}
if (httpGraphQLResponse.body.kind === 'chunked') {
throw new RestfulApiHttpError('Apollo returned chunked result').withHttpStatusCode(500).withRequestId(ContextUtil.currentRequestId());
}
const bodyAsString = StringRatchet.trimToEmpty(httpGraphQLResponse?.body?.string);
rval = {
body: Base64Ratchet.generateBase64VersionOfString(bodyAsString),
headers: outHeaders,
multiValueHeaders: {},
isBase64Encoded: true,
statusCode: httpGraphQLResponse.status || 200,
};
if (eventMethod === 'GET' && rval.headers['content-type'] !== 'text/html' && bodyAsString.indexOf('<!DOCTYPE html>') >= 0) {
Logger.info('Forcing content type to html for the sandbox page');
rval.headers = rval.headers || {};
rval.headers['content-type'] = 'text/html';
}
if (options.debugOutputCallback) {
await options.debugOutputCallback(rval);
}
return rval;
}
static addApolloFilterToList(filters, apolloPathRegex, apolloServer, options) {
if (filters) {
filters.push((fCtx) => ApolloFilter.handlePathWithApollo(fCtx, apolloPathRegex, apolloServer, options));
}
}
}
//# sourceMappingURL=apollo-filter.js.map