From adcfa21a0490b2f6c15c6b88f44cf0db630460a2 Mon Sep 17 00:00:00 2001 From: Aras Abbasi Date: Mon, 4 Dec 2023 02:10:35 +0100 Subject: [PATCH] fix: improve compatibility of Google Cloud Functions, `req.body` is the parsed JSON payload (#937) --- src/middleware/node/get-payload.ts | 18 +++++++--- test/integration/node-middleware.test.ts | 44 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/middleware/node/get-payload.ts b/src/middleware/node/get-payload.ts index 05c5c1d9..cb873665 100644 --- a/src/middleware/node/get-payload.ts +++ b/src/middleware/node/get-payload.ts @@ -12,11 +12,21 @@ import AggregateError from "aggregate-error"; type IncomingMessage = any; export function getPayload(request: IncomingMessage): Promise { - // If request.body already exists we can stop here - // See https://github.com/octokit/webhooks.js/pull/23 - - if (request.body) return Promise.resolve(request.body); + if ("body" in request) { + if ( + typeof request.body === "object" && + "rawBody" in request && + request.rawBody instanceof Buffer + ) { + // The body is already an Object and rawBody is a Buffer (e.g. GCF) + return Promise.resolve(request.rawBody.toString("utf8")); + } else { + // The body is a String (e.g. Lambda) + return Promise.resolve(request.body); + } + } + // We need to load the payload from the request (normal case of Node.js server) return new Promise((resolve, reject) => { let data: Buffer[] = []; diff --git a/test/integration/node-middleware.test.ts b/test/integration/node-middleware.test.ts index 6f150e69..73c8663c 100644 --- a/test/integration/node-middleware.test.ts +++ b/test/integration/node-middleware.test.ts @@ -654,3 +654,47 @@ describe("createNodeMiddleware(webhooks)", () => { server.close(); }); }); + +test("request.body is already an Object and has request.rawBody as Buffer (e.g. GCF)", async () => { + expect.assertions(3); + + const webhooks = new Webhooks({ + secret: "mySecret", + }); + const dataChunks: any[] = []; + const middleware = createNodeMiddleware(webhooks); + + const server = createServer((req, res) => { + req.once("data", (chunk) => dataChunks.push(chunk)); + req.once("end", () => { + // @ts-expect-error - TS2339: Property 'rawBody' does not exist on type 'IncomingMessage'. + req.rawBody = Buffer.concat(dataChunks); + // @ts-expect-error - TS2339: Property 'body' does not exist on type 'IncomingMessage'. + req.body = JSON.parse(req.rawBody); + middleware(req, res); + }); + }).listen(); + + webhooks.on("push", (event) => { + expect(event.id).toBe("123e4567-e89b-12d3-a456-426655440000"); + }); + + // @ts-expect-error complains about { port } although it's included in returned AddressInfo interface + const { port } = server.address(); + + const response = await fetch(`http://localhost:${port}/api/github/webhooks`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-GitHub-Delivery": "123e4567-e89b-12d3-a456-426655440000", + "X-GitHub-Event": "push", + "X-Hub-Signature-256": signatureSha256, + }, + body: pushEventPayload, + }); + + expect(response.status).toEqual(200); + expect(await response.text()).toEqual("ok\n"); + + server.close(); +});