Skip to content

Commit

Permalink
Expose useful functions (#1955)
Browse files Browse the repository at this point in the history
* oed commit

* Added unit tests for the functions

* fix edge cases

* undo changes
  • Loading branch information
WilliamBergamin authored Sep 21, 2023
1 parent 51ee0b5 commit 7e59dba
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 29 deletions.
30 changes: 2 additions & 28 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
SlashCommand,
WorkflowStepEdit,
} from './types';
import { IncomingEventType, getTypeAndConversation, assertNever } from './helpers';
import { IncomingEventType, getTypeAndConversation, assertNever, isBodyWithTypeEnterpriseInstall, isEventTypeToSkipAuthorize } from './helpers';
import { CodedError, asCodedError, AppInitializationError, MultipleListenerError, ErrorCode, InvalidCustomPropertyError } from './errors';
import { AllMiddlewareArgs, contextBuiltinKeys } from './types/middleware';
import { StringIndexed } from './types/helpers';
Expand Down Expand Up @@ -878,7 +878,7 @@ export default class App<AppCustomContext extends StringIndexed = StringIndexed>
const source = buildSource(type, conversationId, bodyArg, isEnterpriseInstall);

let authorizeResult: AuthorizeResult;
if (type === IncomingEventType.Event && isEventTypeToSkipAuthorize(event.body.event.type)) {
if (type === IncomingEventType.Event && isEventTypeToSkipAuthorize(event)) {
authorizeResult = {
enterpriseId: source.enterpriseId,
teamId: source.teamId,
Expand Down Expand Up @@ -1522,25 +1522,6 @@ function buildSource<IsEnterpriseInstall extends boolean>(
};
}

function isBodyWithTypeEnterpriseInstall(body: AnyMiddlewareArgs['body'], type: IncomingEventType): boolean {
if (type === IncomingEventType.Event) {
const bodyAsEvent = body as SlackEventMiddlewareArgs['body'];
if (Array.isArray(bodyAsEvent.authorizations) && bodyAsEvent.authorizations[0] !== undefined) {
return !!bodyAsEvent.authorizations[0].is_enterprise_install;
}
}
// command payloads have this property set as a string
if (typeof body.is_enterprise_install === 'string') {
return body.is_enterprise_install === 'true';
}
// all remaining types have a boolean property
if (body.is_enterprise_install !== undefined) {
return body.is_enterprise_install;
}
// as a fallback we assume it's a single team installation (but this should never happen)
return false;
}

function isBlockActionOrInteractiveMessageBody(
body: SlackActionMiddlewareArgs['body'],
): body is SlackActionMiddlewareArgs<BlockAction | InteractiveMessage>['body'] {
Expand All @@ -1562,13 +1543,6 @@ function buildRespondFn(
};
}

// token revocation use cases
// https://github.com/slackapi/bolt-js/issues/674
const eventTypesToSkipAuthorize = ['app_uninstalled', 'tokens_revoked'];
function isEventTypeToSkipAuthorize(eventType: string) {
return eventTypesToSkipAuthorize.includes(eventType);
}

function escapeHtml(input: string | undefined | null): string {
if (input) {
return input.replace(/&/g, '&amp;')
Expand Down
115 changes: 114 additions & 1 deletion src/helpers.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'mocha';
import { assert } from 'chai';
import { getTypeAndConversation, IncomingEventType } from './helpers';
import { isBodyWithTypeEnterpriseInstall, getTypeAndConversation, IncomingEventType, isEventTypeToSkipAuthorize } from './helpers';
import { AnyMiddlewareArgs, ReceiverEvent, SlackEventMiddlewareArgs } from './types';

describe('Helpers', () => {
describe('getTypeAndConversation()', () => {
Expand Down Expand Up @@ -110,6 +111,118 @@ describe('Helpers', () => {
});
});
});

describe(`${isBodyWithTypeEnterpriseInstall.name}()`, () => {
describe('with body of event type', () => {
// Arrange
const dummyEventBody: SlackEventMiddlewareArgs['body'] = {
token: '',
team_id: '',
api_app_id: '',
event_id: '',
event_time: 0,
type: 'event_callback',
event: {
type: 'app_home_opened',
user: '',
channel: '',
event_ts: '',
},
authorizations: [{
enterprise_id: '',
is_bot: true,
team_id: '',
user_id: '',
is_enterprise_install: true,
}],
};

it('should resolve the is_enterprise_install field', () => {
// Act
const isEnterpriseInstall = isBodyWithTypeEnterpriseInstall(dummyEventBody);
// Assert
assert(isEnterpriseInstall === true);
});

it('should resolve the is_enterprise_install with provided event type', () => {
// Act
const isEnterpriseInstall = isBodyWithTypeEnterpriseInstall(dummyEventBody, IncomingEventType.Event);
// Assert
assert(isEnterpriseInstall === true);
});
});

describe('with is_enterprise_install as a string value', () => {
// Arrange
const dummyEventBody = {
is_enterprise_install: 'true',
} as AnyMiddlewareArgs['body'];

it('should resolve is_enterprise_install as truthy', () => {
// Act
const isEnterpriseInstall = isBodyWithTypeEnterpriseInstall(dummyEventBody);
// Assert
assert(isEnterpriseInstall === true);
});
});

describe('with is_enterprise_install as boolean value', () => {
// Arrange
const dummyEventBody = {
is_enterprise_install: true,
} as AnyMiddlewareArgs['body'];

it('should resolve is_enterprise_install as truthy', () => {
// Act
const isEnterpriseInstall = isBodyWithTypeEnterpriseInstall(dummyEventBody);
// Assert
assert(isEnterpriseInstall === true);
});
});

describe('with is_enterprise_install undefined', () => {
// Arrange
const dummyEventBody = {} as AnyMiddlewareArgs['body'];

it('should resolve is_enterprise_install as falsy', () => {
// Act
const isEnterpriseInstall = isBodyWithTypeEnterpriseInstall(dummyEventBody);
// Assert
assert(isEnterpriseInstall === false);
});
});
});

describe(`${isEventTypeToSkipAuthorize.name}()`, () => {
describe('receiver events that can be skipped', () => {
it('should return truthy when event can be skipped', () => {
// Arrange
const dummyEventBody = { ack: async () => { }, body: { event: { type: 'app_uninstalled' } } } as ReceiverEvent;
// Act
const isEnterpriseInstall = isEventTypeToSkipAuthorize(dummyEventBody);
// Assert
assert(isEnterpriseInstall === true);
});

it('should return falsy when event can not be skipped', () => {
// Arrange
const dummyEventBody = { ack: async () => { }, body: { event: { type: '' } } } as ReceiverEvent;
// Act
const isEnterpriseInstall = isEventTypeToSkipAuthorize(dummyEventBody);
// Assert
assert(isEnterpriseInstall === false);
});

it('should return falsy when event is invalid', () => {
// Arrange
const dummyEventBody = { ack: async () => { }, body: {} } as ReceiverEvent;
// Act
const isEnterpriseInstall = isEventTypeToSkipAuthorize(dummyEventBody);
// Assert
assert(isEnterpriseInstall === false);
});
});
});
});

function createFakeActions(conversationId: string): any[] {
Expand Down
43 changes: 43 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
SlackAction,
OptionsSource,
MessageShortcut,
AnyMiddlewareArgs,
ReceiverEvent,
} from './types';

/**
Expand All @@ -22,6 +24,11 @@ export enum IncomingEventType {
Shortcut,
}

// ----------------------------
// For skipping authorize with event

const eventTypesToSkipAuthorize = ['app_uninstalled', 'tokens_revoked'];

/**
* Helper which finds the type and channel (if any) that any specific incoming event is related to.
*
Expand Down Expand Up @@ -101,6 +108,42 @@ export function getTypeAndConversation(body: any): { type?: IncomingEventType; c
return {};
}

/**
* Helper which determines if the body of a request is enterprise install.
*
* Providing the type is optional but if you do the execution will be faster
*/
export function isBodyWithTypeEnterpriseInstall(body: AnyMiddlewareArgs['body'], type?: IncomingEventType): boolean {
const _type = type !== undefined ? type : getTypeAndConversation(body).type;

if (_type === IncomingEventType.Event) {
const bodyAsEvent = body as SlackEventMiddlewareArgs['body'];
if (Array.isArray(bodyAsEvent.authorizations) && bodyAsEvent.authorizations[0] !== undefined) {
return !!bodyAsEvent.authorizations[0].is_enterprise_install;
}
}
// command payloads have this property set as a string
if (typeof body.is_enterprise_install === 'string') {
return body.is_enterprise_install === 'true';
}
// all remaining types have a boolean property
if (body.is_enterprise_install !== undefined) {
return body.is_enterprise_install;
}
// as a fallback we assume it's a single team installation (but this should never happen)
return false;
}

/**
* Helper which determines if the event type will skip Authorize.
*
* Token revocation use cases
* https://github.com/slackapi/bolt-js/issues/674
*/
export function isEventTypeToSkipAuthorize(event: ReceiverEvent): boolean {
return eventTypesToSkipAuthorize.includes(event.body.event?.type);
}

/* istanbul ignore next */

/** Helper that should never be called, but is useful for exhaustiveness checking in conditional branches */
Expand Down

0 comments on commit 7e59dba

Please sign in to comment.