diff --git a/src/event-handler/index.ts b/src/event-handler/index.ts index 7f2d6e32..25023ea5 100644 --- a/src/event-handler/index.ts +++ b/src/event-handler/index.ts @@ -1,4 +1,8 @@ -import { receiverOn as on } from "./on"; +import { + receiverOn as on, + receiverOnAny as onAny, + receiverOnError as onError, +} from "./on"; import { receiverHandle as receive } from "./receive"; import { removeListener } from "./remove-listener"; import { Options, State } from "../types"; @@ -14,6 +18,8 @@ export function createEventHandler(options: Options) { return { on: on.bind(null, state), + onAny: onAny.bind(null, state), + onError: onError.bind(null, state), removeListener: removeListener.bind(null, state), receive: receive.bind(null, state), }; diff --git a/src/event-handler/on.ts b/src/event-handler/on.ts index bd3d0d98..cf7e0ddf 100644 --- a/src/event-handler/on.ts +++ b/src/event-handler/on.ts @@ -1,7 +1,18 @@ import { WebhookEvents } from "../generated/get-webhook-payload-type-from-event"; import { webhookNames } from "../generated/webhook-names"; -import { State } from "../types"; +import { State, WebhookEvent, WebhookEventHandlerError } from "../types"; +function handleEventHandlers( + state: State, + webhookName: WebhookEvents, + handler: Function +) { + if (!state.hooks[webhookName]) { + state.hooks[webhookName] = []; + } + + state.hooks[webhookName].push(handler); +} export function receiverOn( state: State, webhookNameOrNames: WebhookEvents | WebhookEvents[], @@ -20,9 +31,28 @@ export function receiverOn( ); } - if (!state.hooks[webhookNameOrNames]) { - state.hooks[webhookNameOrNames] = []; + if (webhookNameOrNames === "*" || webhookNameOrNames === "error") { + const webhookName = webhookNameOrNames === "*" ? "any" : webhookNameOrNames; + console.warn( + `Using the "${webhookNameOrNames}" event with the regular Webhooks.on() function is deprecated. Please use the Webhooks.on${ + webhookName.charAt(0).toUpperCase() + webhookName.slice(1) + }() method instead` + ); } - state.hooks[webhookNameOrNames].push(handler); + handleEventHandlers(state, webhookNameOrNames, handler); +} + +export function receiverOnAny( + state: State, + handler: (event: WebhookEvent) => any +) { + handleEventHandlers(state, "*", handler); +} + +export function receiverOnError( + state: State, + handler: (event: WebhookEventHandlerError) => any +) { + handleEventHandlers(state, "error", handler); } diff --git a/src/index.ts b/src/index.ts index 44fb6d6e..6ec2fb42 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ import { State, WebhookEvent, WebhookError, + WebhookEventHandlerError, HandlerFunction, } from "./types"; import { IncomingMessage, ServerResponse } from "http"; @@ -22,6 +23,8 @@ class Webhooks { event: E | E[], callback: HandlerFunction ) => void; + public onAny: (callback: (event: WebhookEvent) => any) => void; + public onError: (callback: (event: WebhookEventHandlerError) => any) => void; public removeListener: ( event: E | E[], callback: HandlerFunction @@ -55,6 +58,8 @@ class Webhooks { this.sign = sign.bind(null, options.secret); this.verify = verify.bind(null, options.secret); this.on = state.eventHandler.on; + this.onAny = state.eventHandler.onAny; + this.onError = state.eventHandler.onError; this.removeListener = state.eventHandler.removeListener; this.receive = state.eventHandler.receive; this.middleware = middleware.bind(null, state); diff --git a/test/integration/event-handler-test.ts b/test/integration/event-handler-test.ts index 10c2996d..43084af0 100644 --- a/test/integration/event-handler-test.ts +++ b/test/integration/event-handler-test.ts @@ -1,7 +1,7 @@ import { createEventHandler } from "../../src/event-handler"; import pushEventPayload from "../fixtures/push-payload.json"; import installationCreatedPayload from "../fixtures/installation-created-payload.json"; -import { WebhookError, WebhookEvent } from "../../src/types"; +import { WebhookEvent, WebhookEventHandlerError } from "../../src/types"; test("events", (done) => { const eventHandler = createEventHandler({}); @@ -37,7 +37,7 @@ test("events", (done) => { eventHandler.on(["push"], hook4); eventHandler.on("installation", hook5); eventHandler.on("installation.created", hook6); - eventHandler.on("*", hook7); + eventHandler.onAny(hook7); eventHandler.removeListener("push", hook3); eventHandler.removeListener(["push"], hook4); @@ -68,7 +68,7 @@ test("events", (done) => { "* (installation)", ]); - eventHandler.on("error", (error: WebhookError) => { + eventHandler.onError((error: WebhookEventHandlerError) => { expect(error.event.payload).toBeTruthy(); // t.pass("error event triggered"); expect(error.message).toMatch(/oops/); diff --git a/test/integration/server-test.ts b/test/integration/server-test.ts index 2a314953..9c4d2977 100644 --- a/test/integration/server-test.ts +++ b/test/integration/server-test.ts @@ -142,7 +142,7 @@ describe("server-test", () => { }); const server = http.createServer(api.middleware); const errorHandler = jest.fn(); - api.on("error", errorHandler); + api.onError(errorHandler); promisify(server.listen.bind(server))(availablePort) @@ -177,7 +177,7 @@ describe("server-test", () => { }); const server = http.createServer(api.middleware); const errorHandler = jest.fn(); - api.on("error", errorHandler); + api.onError(errorHandler); promisify(server.listen.bind(server))(availablePort) diff --git a/test/typescript-validate.ts b/test/typescript-validate.ts index f3a90891..9af72de3 100644 --- a/test/typescript-validate.ts +++ b/test/typescript-validate.ts @@ -69,12 +69,19 @@ export default async function () { verify("randomSecret", {}, "randomSignature"); + // This is deprecated usage webhooks.on("*", ({ id, name, payload }) => { console.log(name, "event received"); const sig = webhooks.sign(payload); webhooks.verify(payload, sig); }); + webhooks.onAny(({ id, name, payload }) => { + console.log(name, "event received"); + const sig = webhooks.sign(payload); + webhooks.verify(payload, sig); + }); + webhooks.on("check_run.completed", () => {}); webhooks.on( @@ -124,6 +131,7 @@ export default async function () { console.log(event.foo); }); + // This is deprecated usage webhooks.on("error", (error) => { console.log(error.event.name); const [firstError] = Array.from(error); @@ -132,6 +140,14 @@ export default async function () { console.log(firstError.request); }); + webhooks.onError((error) => { + console.log(error.event.name); + const [firstError] = Array.from(error); + console.log(firstError.status); + console.log(firstError.headers); + console.log(firstError.request); + }); + createServer(webhooks.middleware).listen(3000); } diff --git a/test/unit/event-handler-on-test.ts b/test/unit/event-handler-on-test.ts index 243149fe..c94738fd 100644 --- a/test/unit/event-handler-on-test.ts +++ b/test/unit/event-handler-on-test.ts @@ -20,3 +20,21 @@ test("receiver.on with invalid event name", () => { simple.restore(); }); + +test("receiver.on with event name of '*' logs deprecation notice", () => { + simple.mock(console, "warn").callFn(function () {}); + receiverOn(state, "*", noop); + expect((console.warn as simple.Stub).callCount).toBe(1); + expect((console.warn as simple.Stub).lastCall.arg).toBe( + 'Using the "*" event with the regular Webhooks.on() function is deprecated. Please use the Webhooks.onAny() method instead' + ); +}); + +test("receiver.on with event name of 'error' logs deprecation notice", () => { + simple.mock(console, "warn").callFn(function () {}); + receiverOn(state, "error", noop); + expect((console.warn as simple.Stub).callCount).toBe(1); + expect((console.warn as simple.Stub).lastCall.arg).toBe( + 'Using the "error" event with the regular Webhooks.on() function is deprecated. Please use the Webhooks.onError() method instead' + ); +});