From 6c0de603bf20272573135d73bc6ac5523b4abd68 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Wed, 5 Oct 2022 18:25:21 -0400 Subject: [PATCH] Fix context.team_id for view interactions in a Slack Connect channel (#1615) * Added app_installed_team_id handling --- src/App-basic-features.spec.ts | 58 +++++++++++++++++++++++++++++++++- src/App.ts | 45 ++++++++++++++++++-------- 2 files changed, 88 insertions(+), 15 deletions(-) diff --git a/src/App-basic-features.spec.ts b/src/App-basic-features.spec.ts index 282b14c4b..7559d3157 100644 --- a/src/App-basic-features.spec.ts +++ b/src/App-basic-features.spec.ts @@ -1150,13 +1150,69 @@ describe('App basic features', () => { ]; // Act - await Promise.all(receiverEvents.map((event) => fakeReceiver.sendEvent(event))); + await Promise.all( + receiverEvents.map((event) => fakeReceiver.sendEvent(event)), + ); // Assert assert.isTrue(fakeLogger.info.called); assert.isTrue(ackInMiddlewareCalled); }); }); + + describe('context', () => { + it('should be able to use the app_installed_team_id when provided by the payload', async () => { + // Arrange + const fakeAxiosPost = sinon.fake.resolves({}); + overrides = buildOverrides([ + withNoopWebClient(), + withAxiosPost(fakeAxiosPost), + ]); + const MockApp = await importApp(overrides); + + // Act + const app = new MockApp({ + receiver: fakeReceiver, + authorize: sinon.fake.resolves(dummyAuthorizationResult), + }); + + app.view('view-id', async ({ ack, context, view }) => { + assert.equal('T-installed-workspace', context.teamId); + assert.notEqual('T-installed-workspace', view.team_id); + await ack(); + }); + app.error(fakeErrorHandler); + + let ackCalled = false; + + const receiverEvent = { + ack: async () => { + ackCalled = true; + }, + body: { + type: 'view_submission', + team: {}, + user: {}, + view: { + id: 'V111', + type: 'modal', + callback_id: 'view-id', + state: {}, + title: {}, + close: {}, + submit: {}, + app_installed_team_id: 'T-installed-workspace', + }, + }, + }; + + // Act + await fakeReceiver.sendEvent(receiverEvent); + + // Assert + assert.isTrue(ackCalled); + }); + }); }); }); diff --git a/src/App.ts b/src/App.ts index a7aeb62eb..573ade950 100644 --- a/src/App.ts +++ b/src/App.ts @@ -1368,29 +1368,46 @@ function buildSource( return (body as SlackCommandMiddlewareArgs['body']).team_id; } + const parseTeamId = ( + bodyAs: + | SlackAction + | SlackViewAction + | SlackShortcut + | KnownOptionsPayloadFromType, + ): string | undefined => { + // When the app is installed using org-wide deployment, team property will be null + if (typeof bodyAs.team !== 'undefined' && bodyAs.team !== null) { + return bodyAs.team.id; + } + + // This is the only place where this function might return undefined + return bodyAs.user.team_id; + }; + + if (type === IncomingEventType.ViewAction) { + // view_submission/closed payloads can have `view.app_installed_team_id` when a modal view that was opened + // in a different workspace via some operations inside a Slack Connect channel. + + const bodyAsView = body as SlackViewMiddlewareArgs['body']; + + if (bodyAsView.view.app_installed_team_id) { + return bodyAsView.view.app_installed_team_id; + } + + return parseTeamId(bodyAsView); + } + if ( type === IncomingEventType.Action || type === IncomingEventType.Options || - type === IncomingEventType.ViewAction || type === IncomingEventType.Shortcut ) { - const bodyAsActionOrOptionsOrViewActionOrShortcut = body as ( + const bodyAsActionOrOptionsOrShortcut = body as ( | SlackActionMiddlewareArgs | SlackOptionsMiddlewareArgs - | SlackViewMiddlewareArgs | SlackShortcutMiddlewareArgs )['body']; - - // When the app is installed using org-wide deployment, team property will be null - if ( - typeof bodyAsActionOrOptionsOrViewActionOrShortcut.team !== 'undefined' && - bodyAsActionOrOptionsOrViewActionOrShortcut.team !== null - ) { - return bodyAsActionOrOptionsOrViewActionOrShortcut.team.id; - } - - // This is the only place where this function might return undefined - return bodyAsActionOrOptionsOrViewActionOrShortcut.user.team_id; + return parseTeamId(bodyAsActionOrOptionsOrShortcut); } return assertNever(type);