From 29aeedcbcefac210ad72d69bcee7023aa9c236d6 Mon Sep 17 00:00:00 2001 From: Ernesto Resende Date: Thu, 12 Jan 2023 00:16:16 -0300 Subject: [PATCH] feat: initial package implementation --- src/index.ts | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 9fdef3b..e3ff08c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,67 @@ -export function helloWorld() { - return 'Hello World!'; +import { + ApolloServer, + BaseContext, + ContextFunction, + HeaderMap, +} from '@apollo/server'; +import { parse } from 'url'; +import { http, type HttpFunction } from '@google-cloud/functions-framework'; +import type { WithRequired } from '@apollo/utils.withrequired'; + +interface Options { + context?: ContextFunction, Context>; + functionName: string; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const defaultContext: ContextFunction<[], any> = async () => ({}); + +export function startServerAndCreateGoogleCloudFunctionsHandler( + server: ApolloServer, + options: Options, +): any; +export function startServerAndCreateGoogleCloudFunctionsHandler< + Context extends BaseContext, +>( + server: ApolloServer, + options: WithRequired, 'context'>, +): any; +export function startServerAndCreateGoogleCloudFunctionsHandler< + Context extends BaseContext, +>(server: ApolloServer, options: Options) { + server.startInBackgroundHandlingStartupErrorsByLoggingAndFailingAllRequests(); + + const contextFunction = options?.context || defaultContext; + const handler = http(options.functionName, async (req, res) => { + const headers = new HeaderMap(); + + for (const [key, value] of Object.entries(req.headers)) { + if (typeof value === 'string') { + headers.set(key, value); + } + } + + const httpGraphQLResponse = await server.executeHTTPGraphQLRequest({ + context: () => contextFunction(req, res), + httpGraphQLRequest: { + body: req.body, + headers, + method: req.method || 'POST', + search: req.url ? parse(req.url).search || '' : '', + }, + }); + + res.statusCode = httpGraphQLResponse.status || 200; + + if (httpGraphQLResponse.body.kind === 'complete') { + res.send(httpGraphQLResponse.body.string); + } else { + for await (const chunk of httpGraphQLResponse.body.asyncIterator) { + res.write(chunk); + } + res.end(); + } + }); + + return handler; }