Skip to content

Commit

Permalink
Fixed #284 -added support for regexp to events
Browse files Browse the repository at this point in the history
  • Loading branch information
pdontha committed Jan 21, 2021
1 parent 09e6728 commit b0378dd
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 65 deletions.
79 changes: 42 additions & 37 deletions src/App.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1056,53 +1056,58 @@ describe('App', () => {
await ackFn();
await next!();
});
app.shortcut({ callback_id: 'message_action_callback_id' }, async ({}) => {
app.shortcut({ callback_id: 'message_action_callback_id' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ type: 'message_action', callback_id: 'another_message_action_callback_id' }, async ({}) => {
app.shortcut({ type: 'message_action', callback_id: 'another_message_action_callback_id' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ type: 'message_action', callback_id: 'does_not_exist' }, async ({}) => {
app.shortcut({ type: 'message_action', callback_id: 'does_not_exist' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ callback_id: 'shortcut_callback_id' }, async ({}) => {
app.shortcut({ callback_id: 'shortcut_callback_id' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ type: 'shortcut', callback_id: 'another_shortcut_callback_id' }, async ({}) => {
app.shortcut({ type: 'shortcut', callback_id: 'another_shortcut_callback_id' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ type: 'shortcut', callback_id: 'does_not_exist' }, async ({}) => {
app.shortcut({ type: 'shortcut', callback_id: 'does_not_exist' }, async ({ }) => {
await shortcutFn();
});
app.action('block_action_id', async ({}) => {
app.action('block_action_id', async ({ }) => {
await actionFn();
});
app.action({ callback_id: 'interactive_message_callback_id' }, async ({}) => {
app.action({ callback_id: 'interactive_message_callback_id' }, async ({ }) => {
await actionFn();
});
app.action({ callback_id: 'dialog_submission_callback_id' }, async ({}) => {
app.action({ callback_id: 'dialog_submission_callback_id' }, async ({ }) => {
await actionFn();
});
app.view('view_callback_id', async ({}) => {
app.view('view_callback_id', async ({ }) => {
await viewFn();
});
app.view({ callback_id: 'view_callback_id', type: 'view_closed' }, async ({}) => {
app.view({ callback_id: 'view_callback_id', type: 'view_closed' }, async ({ }) => {
await viewFn();
});
app.options('external_select_action_id', async ({}) => {
app.options('external_select_action_id', async ({ }) => {
await optionsFn();
});
app.options({ callback_id: 'dialog_suggestion_callback_id' }, async ({}) => {
app.options({ callback_id: 'dialog_suggestion_callback_id' }, async ({ }) => {
await optionsFn();
});

app.event('app_home_opened', async ({}) => {
app.event('app_home_opened', async ({ }) => {
/* noop */
});
app.message('hello', async ({}) => {

app.event(/app_home_opened|app_mention/, async ({ }) => {
/* noop */
});

app.message('hello', async ({ }) => {
/* noop */
});
app.command('/echo', async ({}) => {
app.command('/echo', async ({ }) => {
/* noop */
});

Expand All @@ -1112,7 +1117,7 @@ describe('App', () => {
type: 'view_submission',
unknown_key: 'should be detected',
} as any) as ViewConstraints;
app.view(invalidViewConstraints1, async ({}) => {
app.view(invalidViewConstraints1, async ({ }) => {
/* noop */
});
assert.isTrue(fakeLogger.error.called);
Expand All @@ -1124,7 +1129,7 @@ describe('App', () => {
type: undefined,
unknown_key: 'should be detected',
} as any) as ViewConstraints;
app.view(invalidViewConstraints2, async ({}) => {
app.view(invalidViewConstraints2, async ({ }) => {
/* noop */
});
assert.isTrue(fakeLogger.error.called);
Expand Down Expand Up @@ -1165,53 +1170,53 @@ describe('App', () => {
await ackFn();
await next!();
});
app.shortcut({ callback_id: 'message_action_callback_id' }, async ({}) => {
app.shortcut({ callback_id: 'message_action_callback_id' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ type: 'message_action', callback_id: 'another_message_action_callback_id' }, async ({}) => {
app.shortcut({ type: 'message_action', callback_id: 'another_message_action_callback_id' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ type: 'message_action', callback_id: 'does_not_exist' }, async ({}) => {
app.shortcut({ type: 'message_action', callback_id: 'does_not_exist' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ callback_id: 'shortcut_callback_id' }, async ({}) => {
app.shortcut({ callback_id: 'shortcut_callback_id' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ type: 'shortcut', callback_id: 'another_shortcut_callback_id' }, async ({}) => {
app.shortcut({ type: 'shortcut', callback_id: 'another_shortcut_callback_id' }, async ({ }) => {
await shortcutFn();
});
app.shortcut({ type: 'shortcut', callback_id: 'does_not_exist' }, async ({}) => {
app.shortcut({ type: 'shortcut', callback_id: 'does_not_exist' }, async ({ }) => {
await shortcutFn();
});
app.action('block_action_id', async ({}) => {
app.action('block_action_id', async ({ }) => {
await actionFn();
});
app.action({ callback_id: 'interactive_message_callback_id' }, async ({}) => {
app.action({ callback_id: 'interactive_message_callback_id' }, async ({ }) => {
await actionFn();
});
app.action({ callback_id: 'dialog_submission_callback_id' }, async ({}) => {
app.action({ callback_id: 'dialog_submission_callback_id' }, async ({ }) => {
await actionFn();
});
app.view('view_callback_id', async ({}) => {
app.view('view_callback_id', async ({ }) => {
await viewFn();
});
app.view({ callback_id: 'view_callback_id', type: 'view_closed' }, async ({}) => {
app.view({ callback_id: 'view_callback_id', type: 'view_closed' }, async ({ }) => {
await viewFn();
});
app.options('external_select_action_id', async ({}) => {
app.options('external_select_action_id', async ({ }) => {
await optionsFn();
});
app.options({ callback_id: 'dialog_suggestion_callback_id' }, async ({}) => {
app.options({ callback_id: 'dialog_suggestion_callback_id' }, async ({ }) => {
await optionsFn();
});

app.event('app_home_opened', async ({}) => {
app.event('app_home_opened', async ({ }) => {
/* noop */
});
app.message('hello', async ({}) => {
app.message('hello', async ({ }) => {
/* noop */
});
app.command('/echo', async ({}) => {
app.command('/echo', async ({ }) => {
/* noop */
});

Expand All @@ -1221,7 +1226,7 @@ describe('App', () => {
type: 'view_submission',
unknown_key: 'should be detected',
} as any) as ViewConstraints;
app.view(invalidViewConstraints1, async ({}) => {
app.view(invalidViewConstraints1, async ({ }) => {
/* noop */
});
assert.isTrue(fakeLogger.error.called);
Expand All @@ -1233,7 +1238,7 @@ describe('App', () => {
type: undefined,
unknown_key: 'should be detected',
} as any) as ViewConstraints;
app.view(invalidViewConstraints2, async ({}) => {
app.view(invalidViewConstraints2, async ({ }) => {
/* noop */
});
assert.isTrue(fakeLogger.error.called);
Expand Down Expand Up @@ -1758,7 +1763,7 @@ async function importApp(
function withNoopWebClient(): Override {
return {
'@slack/web-api': {
WebClient: class {},
WebClient: class { },
},
};
}
Expand Down
55 changes: 35 additions & 20 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
SlackShortcutMiddlewareArgs,
SlackViewMiddlewareArgs,
SlackAction,
EventTypePattern,
SlackShortcut,
Context,
SayFn,
Expand Down Expand Up @@ -303,7 +304,7 @@ export default class App {
// No custom receiver
throw new AppInitializationError(
'Signing secret not found, so could not initialize the default receiver. Set a signing secret or use a ' +
'custom receiver.',
'custom receiver.',
);
} else {
this.logger.debug('Initializing HTTPReceiver');
Expand Down Expand Up @@ -410,8 +411,22 @@ export default class App {
public event<EventType extends string = string>(
eventName: EventType,
...listeners: Middleware<SlackEventMiddlewareArgs<EventType>>[]
): void;
public event<EventType extends RegExp = RegExp>(
eventName: EventType,
...listeners: Middleware<SlackEventMiddlewareArgs<string>>[]
): void;
public event<EventType extends EventTypePattern = EventTypePattern>(
...patternsOrMiddleware: (EventType | Middleware<SlackEventMiddlewareArgs<string>>)[]
): void {
this.listeners.push([onlyEvents, matchEventType(eventName), ...listeners] as Middleware<AnyMiddlewareArgs>[]);
const eventMiddleware = patternsOrMiddleware.map((patternOrMiddleware) => {
if (typeof patternOrMiddleware === 'string' || util.types.isRegExp(patternOrMiddleware)) {
return matchEventType(patternOrMiddleware);
}
return patternOrMiddleware;
});

this.listeners.push([onlyEvents, ...eventMiddleware] as Middleware<AnyMiddlewareArgs>[]);
}

// TODO: just make a type alias for Middleware<SlackEventMiddlewareArgs<'message'>>
Expand Down Expand Up @@ -663,19 +678,19 @@ export default class App {
type === IncomingEventType.Event
? (bodyArg as SlackEventMiddlewareArgs['body']).event
: type === IncomingEventType.ViewAction
? (bodyArg as SlackViewMiddlewareArgs['body']).view
: type === IncomingEventType.Shortcut
? (bodyArg as SlackShortcutMiddlewareArgs['body'])
: type === IncomingEventType.Action &&
isBlockActionOrInteractiveMessageBody(bodyArg as SlackActionMiddlewareArgs['body'])
? (bodyArg as SlackActionMiddlewareArgs<BlockAction | InteractiveMessage>['body']).actions[0]
: (bodyArg as (
| Exclude<
AnyMiddlewareArgs,
SlackEventMiddlewareArgs | SlackActionMiddlewareArgs | SlackViewMiddlewareArgs
>
| SlackActionMiddlewareArgs<Exclude<SlackAction, BlockAction | InteractiveMessage>>
)['body']),
? (bodyArg as SlackViewMiddlewareArgs['body']).view
: type === IncomingEventType.Shortcut
? (bodyArg as SlackShortcutMiddlewareArgs['body'])
: type === IncomingEventType.Action &&
isBlockActionOrInteractiveMessageBody(bodyArg as SlackActionMiddlewareArgs['body'])
? (bodyArg as SlackActionMiddlewareArgs<BlockAction | InteractiveMessage>['body']).actions[0]
: (bodyArg as (
| Exclude<
AnyMiddlewareArgs,
SlackEventMiddlewareArgs | SlackActionMiddlewareArgs | SlackViewMiddlewareArgs
>
| SlackActionMiddlewareArgs<Exclude<SlackAction, BlockAction | InteractiveMessage>>
)['body']),
};

// Set aliases
Expand Down Expand Up @@ -1018,11 +1033,11 @@ function singleAuthorization(
authorization.botUserId !== undefined && authorization.botId !== undefined
? Promise.resolve({ botUserId: authorization.botUserId, botId: authorization.botId })
: client.auth.test({ token: authorization.botToken }).then((result) => {
return {
botUserId: result.user_id as string,
botId: result.bot_id as string,
};
});
return {
botUserId: result.user_id as string,
botId: result.bot_id as string,
};
});

return async ({ isEnterpriseInstall }) => {
return { isEnterpriseInstall, botToken: authorization.botToken, ...(await identifiers) };
Expand Down
74 changes: 73 additions & 1 deletion src/middleware/builtin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from '../types';
import { onlyCommands, onlyEvents, matchCommandName, matchEventType, subtype } from './builtin';
import { SlashCommand } from '../types/command';
import { AppMentionEvent } from '../types/events';
import { AppMentionEvent, AppHomeOpenedEvent } from '../types/events';
import { GenericMessageEvent } from '../types/events/message-events';
import { WebClient } from '@slack/web-api';
import { Logger } from '@slack/logger';
Expand Down Expand Up @@ -597,6 +597,27 @@ describe('matchEventType', () => {
};
}

function buildArgsAppHomeOpened(): SlackEventMiddlewareArgs<'app_home_opened'> & {
event?: SlackEvent;
} {
return {
payload: appHomeOpenedEvent,
event: appHomeOpenedEvent,
message: null as never, // a bit hackey to sartisfy TS compiler
body: {
token: 'token-value',
team_id: 'T1234567',
api_app_id: 'A1234567',
event: appHomeOpenedEvent,
type: 'event_callback',
event_id: 'event-id-value',
event_time: 123,
authed_users: [],
},
say: sayNoop,
};
}

it('should detect valid requests', async () => {
const fakeNext = sinon.fake();
await matchEventType('app_mention')({
Expand All @@ -609,6 +630,30 @@ describe('matchEventType', () => {
assert.isTrue(fakeNext.called);
});

it('should detect valid RegExp requests with app_mention', async () => {
const fakeNext = sinon.fake();
await matchEventType(/app_mention|app_home_opened/)({
logger,
client,
next: fakeNext,
context: {},
...buildArgs(),
});
assert.isTrue(fakeNext.called);
});

it('should detect valid RegExp requests with app_home_opened', async () => {
const fakeNext = sinon.fake();
await matchEventType(/app_mention|app_home_opened/)({
logger,
client,
next: fakeNext,
context: {},
...buildArgsAppHomeOpened(),
});
assert.isTrue(fakeNext.called);
});

it('should skip other requests', async () => {
const fakeNext = sinon.fake();
await matchEventType('app_home_opened')({
Expand All @@ -620,6 +665,18 @@ describe('matchEventType', () => {
});
assert.isFalse(fakeNext.called);
});

it('should skip other requests for RegExp', async () => {
const fakeNext = sinon.fake();
await matchEventType(/foo/)({
logger,
client,
next: fakeNext,
context: {},
...buildArgs(),
});
assert.isFalse(fakeNext.called);
});
});

describe('subtype', () => {
Expand Down Expand Up @@ -749,6 +806,21 @@ const appMentionEvent: AppMentionEvent = {
thread_ts: '123.123',
};

const appHomeOpenedEvent: AppHomeOpenedEvent = {
type: 'app_home_opened',
user: 'USERNAME',
channel: 'U1234567',
tab: 'home',
view: {
type: 'home',
blocks: [],
clear_on_close: false,
notify_on_close: false,
external_id: '',
},
event_ts: '123.123',
};

const botMessageEvent: MessageEvent = {
type: 'message',
subtype: 'bot_message',
Expand Down
Loading

0 comments on commit b0378dd

Please sign in to comment.