Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/v3' into v3
Browse files Browse the repository at this point in the history
  • Loading branch information
aoberoi committed Jan 13, 2021
2 parents 4f930b1 + 48fbb3c commit aa7626b
Show file tree
Hide file tree
Showing 11 changed files with 466 additions and 102 deletions.
2 changes: 1 addition & 1 deletion .github/maintainers_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ When documentation is in a beta state, it requires a new, distinct collection of

2. Merge into main repository
* Create a pull request with the commit that was just made. Be certain to include the tag. For
example: `git push username main:rel-v1.0.8 && git push --tags username`.
example: `git push username main --tags`.
* Once tests pass and a reviewer has approved, merge the pull request. You will also want to
update your local `main` branch.
* Push the new tag up to origin `git push --tags origin`.
Expand Down
65 changes: 65 additions & 0 deletions docs/_basic/ja_socket_mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: ソケットモードの使用
lang: ja
slug: socket-mode
order: 16
---

<div class="section-content">
[ソケットモード](https://api.slack.com/socket-mode) は、アプリに WebSocket での接続と、そのコネクション経由でのデータ受信を可能とします。コネクションをハンドリングするために @slack/bolt@3.0.0` 以上では `SokcetModeReceiver` というレシーバーが提供されています。ソケットモードを使う前に、アプリの管理画面でソケットモードの機能が有効になっているコオを確認しておいてください。

`SocketModeReceiver` を使う方法は `App` インスタンスの初期化時にコンストラクターに `socketMode: true``appToken: YOUR_APP_TOKEN` を渡すだけです。App Level Token は、アプリ管理画面の **Basic Information** セクションから取得できます。
</div>

```javascript
const { App } = require('@slack/bolt');

const app = new App({
token: process.env.BOT_TOKEN,
socketMode: true,
appToken: process.env.APP_TOKEN,
});

(async () => {
await app.start();
console.log('⚡️ Bolt app started');
})();
```

<details class="secondary-wrapper">
<summary class="section-head" markdown="0">
<h4 class="section-head">ソケットモードレシーバーのカスタム初期化</h4>
</summary>

<div class="secondary-content" markdown="0">

以下のように `@slack/bolt` から `SocketModeReceiver` を import して、カスタムされたインスタンスとして定義することができます。

</div>

```javascript
const { App, SocketModeReceiver } = require('@slack/bolt');

const socketModeReceiver = new SocketModeReceiver({
appToken: process.env.APP_TOKEN,

// OAuth フローの実装を合わせて使う場合は、以下を有効にしてください
// clientId: process.env.CLIENT_ID,
// clientSecret: process.env.CLIENT_SECRET,
// stateSecret: 'my-state-secret',
// scopes: ['channels:read', 'chat:write', 'app_mentions:read', 'channels:manage', 'commands'],
});

const app = new App({
receiver: socketModeReceiver,
// OAuth を使うなら以下の token 指定は不要です
token: process.env.BOT_TOKEN
});

(async () => {
await app.start();
console.log('⚡️ Bolt app started');
})();
```

</details>
4 changes: 2 additions & 2 deletions docs/_basic/socket_mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ order: 16
---

<div class="section-content">
[Socket Mode](https://api.slack.com/socket-mode) allows your app to connect and receive data from Slack via a WebSocket connection. To handle the connection, Bolt for JavaScript includes a `SocketModeReceiver` (in `@slack/bolt@3.0.0` and higher). Before using Socket Mode, be sure to enable it within your app configuration.
[Socket Mode](https://api.slack.com/socket-mode) allows your app to connect and receive data from Slack via a WebSocket connection. To handle the connection, Bolt for JavaScript includes a `SocketModeReceiver` (in `@slack/bolt@3.0.0` and higher). Before using Socket Mode, be sure to enable it within your app configuration.

To use the `SocketModeReceiver`, just pass in `socketMode:true` and `appToken:YOUR_APP_TOKEN` when initializing `App`. You can get your App Level Token in your app configuration under the **Basic Information** section.
</div>
Expand All @@ -15,7 +15,7 @@ To use the `SocketModeReceiver`, just pass in `socketMode:true` and `appToken:YO
const { App } = require('@slack/bolt');

const app = new App({
token: process.env.BOT_TOKEN
token: process.env.BOT_TOKEN,
socketMode: true,
appToken: process.env.APP_TOKEN,
});
Expand Down
2 changes: 1 addition & 1 deletion docs/_tutorials/ja_using-typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ permalink: /ja-jp/tutorial/using-typescript

### 最低必須バージョン

`@slack/bolt` の最新のメジャーバージョンは TypeScript 3.7 以上での利用をサポートしています。
`@slack/bolt` の最新のメジャーバージョンは TypeScript 4.1 以上での利用をサポートしています。
2 changes: 1 addition & 1 deletion examples/socket-mode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
"author": "Slack Technologies, Inc.",
"license": "MIT",
"dependencies": {
"@slack/bolt": "feat-socket-mode"
"@slack/bolt": "3.0.0"
}
}
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@slack/bolt",
"version": "2.6.1",
"version": "2.7.0",
"description": "A framework for building Slack apps, fast.",
"author": "Slack Technologies, Inc.",
"license": "MIT",
Expand Down Expand Up @@ -42,11 +42,11 @@
"url": "https://github.com/slackapi/bolt-js/issues"
},
"dependencies": {
"@slack/logger": "^2.0.0",
"@slack/oauth": "^1.4.0",
"@slack/socket-mode": "feat-socket-mode",
"@slack/types": "^1.9.0",
"@slack/web-api": "^5.14.0",
"@slack/logger": "^3.0.0",
"@slack/oauth": "^2.0.0",
"@slack/socket-mode": "1.0.0",
"@slack/types": "^2.0.0",
"@slack/web-api": "^6.0.0",
"@types/express": "^4.16.1",
"@types/node": ">=12",
"@types/promise.allsettled": "^1.0.3",
Expand Down
112 changes: 57 additions & 55 deletions src/App.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import App, { ViewConstraints } from './App';
import { WebClientOptions, WebClient } from '@slack/web-api';
import { WorkflowStep } from './WorkflowStep';

// TODO: swap out rewiremock for proxyquire to see if it saves execution time
// Utility functions
const noop = () => Promise.resolve(undefined);
const noopMiddleware = async ({ next }: { next: NextFn }) => {
Expand Down Expand Up @@ -68,7 +67,7 @@ describe('App', () => {
assert(authorizeCallback.notCalled, 'Should not call the authorize callback on instantiation');
assert.instanceOf(app, App);
});
it('should fail without a token for single team authorization or authorize callback or oauth installer', async () => {
it('should fail without a token for single team authorization, authorize callback, nor oauth installer', async () => {
// Arrange
const App = await importApp(); // eslint-disable-line @typescript-eslint/naming-convention, no-underscore-dangle, id-blacklist, id-match

Expand Down Expand Up @@ -243,22 +242,25 @@ describe('App', () => {
});

describe('#start', () => {
it('should pass calls through to receiver', async () => {
// Arrange
const dummyReturn = Symbol();
const dummyParams = [Symbol(), Symbol()];
const fakeReceiver = new FakeReceiver();
const App = await importApp(); // eslint-disable-line @typescript-eslint/naming-convention, no-underscore-dangle, id-blacklist, id-match
const app = new App({ receiver: fakeReceiver, authorize: noopAuthorize });
fakeReceiver.start = sinon.fake.returns(dummyReturn);

// Act
const actualReturn = await app.start(...dummyParams);

// Assert
assert.deepEqual(actualReturn, dummyReturn);
assert.deepEqual(dummyParams, fakeReceiver.start.firstCall.args);
});
// The following test case depends on a definition of App that is generic on its Receiver type. This will be
// addressed in the future. It cannot even be left uncommented with the `it.skip()` global because it will fail
// TypeScript compilation as written.
// it('should pass calls through to receiver', async () => {
// // Arrange
// const dummyReturn = Symbol();
// const dummyParams = [Symbol(), Symbol()];
// const fakeReceiver = new FakeReceiver();
// const App = await importApp(); // eslint-disable-line @typescript-eslint/naming-convention, no-underscore-dangle, id-blacklist, id-match
// const app = new App({ receiver: fakeReceiver, authorize: noopAuthorize });
// fakeReceiver.start = sinon.fake.returns(dummyReturn);
// // Act
// const actualReturn = await app.start(...dummyParams);
// // Assert
// assert.deepEqual(actualReturn, dummyReturn);
// assert.deepEqual(dummyParams, fakeReceiver.start.firstCall.args);
// });
// TODO: another test case to take the place of the one above (for coverage until the definition of App is made
// generic).
});

describe('#stop', () => {
Expand Down Expand Up @@ -1054,53 +1056,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 @@ -1110,7 +1112,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 @@ -1122,7 +1124,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 @@ -1163,53 +1165,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 @@ -1219,7 +1221,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 @@ -1231,7 +1233,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 @@ -1756,7 +1758,7 @@ async function importApp(
function withNoopWebClient(): Override {
return {
'@slack/web-api': {
WebClient: class {},
WebClient: class { },
},
};
}
Expand Down
6 changes: 3 additions & 3 deletions src/App.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,12 @@ export default class App {
/**
* Convenience method to call start on the receiver
*
* TODO: args could be defined using a generic constraint from the receiver type
* TODO: should replace ExpressReceiver in type definition with a generic that is constrained to Receiver
*
* @param args receiver-specific start arguments
*/
public start(...args: any[]): Promise<unknown> {
return this.receiver.start(...args);
public start(...args: Parameters<ExpressReceiver['start']>): ReturnType<ExpressReceiver['start']> {
return this.receiver.start(...args) as ReturnType<ExpressReceiver['start']>;
}

public stop(...args: any[]): Promise<unknown> {
Expand Down

0 comments on commit aa7626b

Please sign in to comment.