From 2362b9cb767f769c91c4904961e055fb3ecde9ef Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Mon, 22 Jul 2024 10:50:12 -0400 Subject: [PATCH] fix: do not proxy `then`. fixes #107 (#108) --- src/api-proxy.ts | 7 ++++- src/api-proxy_test.ts | 61 +++++++++++++++++++++++++++++++------------ 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/api-proxy.ts b/src/api-proxy.ts index 59cc385..86730bf 100644 --- a/src/api-proxy.ts +++ b/src/api-proxy.ts @@ -1,6 +1,8 @@ import { BaseSlackAPIClient } from "./base-client.ts"; import { BaseResponse, SlackAPIClient, SlackAPIMethodArgs } from "./types.ts"; +const DO_NOT_PROXY = ["then"]; + type APICallback = { (method: string, payload?: SlackAPIMethodArgs): Promise; }; @@ -46,7 +48,10 @@ export const APIProxy = ( const proxy = new Proxy(objectToProxy, { get(obj, prop) { // We're attempting to access a property that doesn't exist, so create a new nested proxy - if (typeof prop === "string" && !(prop in obj)) { + if ( + typeof prop === "string" && !DO_NOT_PROXY.includes(prop) && + !(prop in obj) + ) { return APIProxy(null, apiCallback, ...path, prop); } diff --git a/src/api-proxy_test.ts b/src/api-proxy_test.ts index 0f72ca3..c9782dc 100644 --- a/src/api-proxy_test.ts +++ b/src/api-proxy_test.ts @@ -3,30 +3,57 @@ import { BaseSlackAPIClient } from "./base-client.ts"; import { SlackAPIMethodArgs } from "./types.ts"; import { assertSpyCall, spy } from "./dev_deps.ts"; -Deno.test("APIProxy", async () => { +Deno.test("APIProxy", async (t) => { const baseClient = new BaseSlackAPIClient("test-token"); - const clientToProxy = { - apiCall: baseClient.apiCall.bind(baseClient), - response: baseClient.response.bind(baseClient), - }; + const generateClientProxy = (client: BaseSlackAPIClient) => ({ + apiCall: client.apiCall.bind(client), + response: client.response.bind(client), + }); + + await t.step("should proxy legit Slack API calls", async () => { + const clientToProxy = generateClientProxy(baseClient); + + const apiCallHandler = (_method: string, _payload?: SlackAPIMethodArgs) => { + return Promise.resolve({ + ok: true, + toFetchResponse: () => new Response(), + }); + }; + const apiCallHandlerSpy = spy(apiCallHandler); - const apiCallHandler = (_method: string, _payload?: SlackAPIMethodArgs) => { - return Promise.resolve({ ok: true, toFetchResponse: () => new Response() }); - }; - const apiCallHandlerSpy = spy(apiCallHandler); + const client = APIProxy(clientToProxy, apiCallHandlerSpy); - const client = APIProxy(clientToProxy, apiCallHandlerSpy); + const payload = { text: "proxied call", channel: "" }; + await client.chat.postMessage(payload); - const payload = { text: "proxied call", channel: "" }; - await client.chat.postMessage(payload); + assertSpyCall(apiCallHandlerSpy, 0, { + args: ["chat.postMessage", payload], + }); - assertSpyCall(apiCallHandlerSpy, 0, { - args: ["chat.postMessage", payload], + await client.admin.apps.approved.list(); + + assertSpyCall(apiCallHandlerSpy, 1, { + args: ["admin.apps.approved.list", undefined], + }); }); - await client.admin.apps.approved.list(); + await t.step("should not proxy Promise methods like `then`", () => { + const clientToProxy = generateClientProxy(baseClient); + + const apiCallHandler = (_method: string, _payload?: SlackAPIMethodArgs) => { + return Promise.resolve({ + ok: true, + toFetchResponse: () => new Response(), + }); + }; + const apiCallHandlerSpy = spy(apiCallHandler); + + const client = APIProxy(clientToProxy, apiCallHandlerSpy); - assertSpyCall(apiCallHandlerSpy, 1, { - args: ["admin.apps.approved.list", undefined], + // re: https://github.com/slackapi/deno-slack-api/issues/107 + // @ts-expect-error client does not have `then` but thenable feature detection at runtime may invoke or test for the method + if (client.then) { + throw new Error("APIProxy should have no `then`!"); + } }); });