Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates for v2 based on feedback #439

Merged
merged 3 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ async function authWithAcme({ payload, context, say, next }) {
// When the user lookup is successful, add the user details to the context
context.user = user;
} catch (error) {
// middleware/listeners continue
if (error.message === 'Not Found') {
// In the real world, you would need to check if the say function was defined, falling back to the respond
// function if not, and then falling back to only logging the error as a last resort.
Expand Down Expand Up @@ -327,7 +326,7 @@ In general, a middleware can run both before and after the remaining middleware
How you use `next` can
have four different effects:

* **To both preprocess and post-process events** - You can choose to do work going _before_ listener functions by putting code
* **To both preprocess and post-process events** - You can choose to do work both _before_ listener functions by putting code
before `await next()` and _after_ by putting code after `await next()`. `await next()` passes control down the middleware
stack in the order it was defined, then back up it in reverse order.

Expand Down
7 changes: 3 additions & 4 deletions docs/_advanced/ja_receiver.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,12 @@ order: 8
| `start()` | None | `Promise` |
| `stop()` | None | `Promise` |

Bolt アプリでは `init()` が 2 回呼び出されます。
* `await app.processEvent(event)` は、解析されたすべての着信リクエストを `onIncomingEvent()` にルーティングする必要があります。これは、Bolt アプリでは `this.receiver.on('message', message => this.onIncomingEvent(message))` として呼び出されます。
* `await app.handleError` は、エラーをグローバルエラーハンドラーにルーティングする必要があります。これは、Bolt アプリでは `this.receiver.on('error', error => this.onGlobalError(error))` として呼び出されます。
Bolt アプリでは `init()` が 2 回呼び出されます。このメソッドはレシーバーに `App` の参照を付与するため、これにより以下の呼び出しが可能になります。
* `await app.processEvent(event)` は Slack から送信されてくるイベントを受け取るたびに呼び出されます。ハンドリングされなかったエラーが発生した場合はそれを throw します。

Bolt アプリを初期化するときにカスタムレシーバーをコンストラクタに渡すことで、そのカスタムレシーバーを使用できます。ここで紹介するのは、基本的なカスタムレシーバーです。

レシーバーについて詳しくは、[組み込み Express レシーバーのソースコード](https://github.com/slackapi/bolt/blob/master/src/ExpressReceiver.ts)をお読みください。
レシーバーについて詳しくは、[組み込み `ExpressReceiver` のソースコード](https://github.com/slackapi/bolt/blob/master/src/ExpressReceiver.ts)をお読みください。
</div>

```javascript
Expand Down
2 changes: 1 addition & 1 deletion docs/_advanced/middleware_global.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ order: 4
---

<div class="section-content">
Global middleware is run for all incoming events before any listener middleware. You can add any number of global middleware to your app by utilizing `app.use(fn({payload,...,next}))`.
Global middleware is run for all incoming events before any listener middleware. You can add any number of global middleware to your app by utilizing `app.use(fn)`. The middleware function `fn` is called with the same arguments as listeners and an additional `next` function.

Both global and listener middleware must call `await next()` to pass control of the execution chain to the next middleware, or call `throw` to pass an error back up the previously-executed middleware chain.

Expand Down
4 changes: 2 additions & 2 deletions docs/_advanced/receiver.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ A receiver is responsible for handling and parsing any incoming events from Slac
| `stop()` | None | `Promise` |

`init()` is called after Bolt for JavaScript app is created. This method gives the receiver a reference to an `App` to store so that it can call:
* `await app.processEvent(event)` whenever your app receives an event from Slack. It will reject if there is an unhandled error.
* `await app.processEvent(event)` whenever your app receives an event from Slack. It will throw if there is an unhandled error.

To use a custom receiver, you can pass it into the constructor when initializing your Bolt for JavaScript app. Here is what a basic custom receiver might look like.

For a more in-depth look at a receiver, [read the source code for the built-in Express receiver](https://github.com/slackapi/bolt/blob/master/src/ExpressReceiver.ts)
For a more in-depth look at a receiver, [read the source code for the built-in `ExpressReceiver`](https://github.com/slackapi/bolt/blob/master/src/ExpressReceiver.ts)
</div>

```javascript
Expand Down
65 changes: 0 additions & 65 deletions docs/_basic/ja_ listening_responding_shortcuts.md

This file was deleted.

133 changes: 133 additions & 0 deletions docs/_basic/ja_listening_responding_shortcuts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
title: ショートカットのリスニング
lang: ja-jp
slug: shortcuts
order: 8
---

<div class="section-content">
`shortcut()` メソッドは、[グローバルショートカット](https://api.slack.com/interactivity/shortcuts/using#global_shortcuts)と[メッセージショートカット](https://api.slack.com/interactivity/shortcuts/using#message_shortcuts)の両方をサポートします。

ショートカットは、テキスト入力エリアや検索バーから起動できる Slack クライアント内の UI エレメントです。グローバルショートカットは、コンポーザーメニューまたは検索メニューから呼び出すことができます。メッセージショートカットは、メッセージのコンテキストメニュー内にあります。`shortcut()` メソッドを使って、これらのショートカットのイベントをリスニングすることができます。このメソッドには `callback_id` を文字列または正規表現のデータ型で設定します。

グローバルショートカットのイベントは Slack へイベントを受信したことを知らせるために `ack()` メソッドで確認する必要があります。

グローバルショートカットのペイロードは、ユーザーの実行アクションの確認のために[モーダルを開く](#creating-modals)などの用途に使用できる `trigger_id` を含んでいます。

⚠️ グローバルショートカットのペイロードは **チャンネル ID は含んでいない** ことに注意してください。もしあなたのアプリがチャンネル ID を知る必要があれば、モーダル内で [`conversations_select`](https://api.slack.com/reference/block-kit/block-elements#conversation_select) エレメントを使用できます。
メッセージショートカットのペイロードはチャンネル ID を含みます。
</div>

```javascript
// open_modal というグローバルショートカットはシンプルなモーダルを開く
app.shortcut('open_modal', async ({ shortcut, ack, context }) => {
// グローバルショートカットリクエストの確認
ack();

try {
// 組み込みの WebClient を使って views.open API メソッドを呼び出す
const result = await app.client.views.open({
// `context` オブジェクトに保持されたトークンを使用
token: context.botToken,
trigger_id: shortcut.trigger_id,
view: {
"type": "modal",
"title": {
"type": "plain_text",
"text": "My App"
},
"close": {
"type": "plain_text",
"text": "Close"
},
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "About the simplest modal you could conceive of :smile:\n\nMaybe <https://api.slack.com/reference/block-kit/interactive-components|*make the modal interactive*> or <https://api.slack.com/surfaces/modals/using#modifying|*learn more advanced modal use cases*>."
}
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "Psssst this modal was designed using <https://api.slack.com/tools/block-kit-builder|*Block Kit Builder*>"
}
]
}
]
}
});

console.log(result);
}
catch (error) {
console.error(error);
}
});
```

<details class="secondary-wrapper">
<summary class="section-head" markdown="0">
<h4 class="section-head">制約付きオブジェクトを使用したショートカットのリスニング</h4>
</summary>

<div class="secondary-content" markdown="0">
制約付きオブジェクトを使って `callback_id` や `type` によるリスニングをすることができます。オブジェクト内の制約は文字列型または RegExp オブジェクトを使用できます。

</div>

```javascript
// callback_id が 'open_modal' と一致し type が 'message_action' と一致する場合のみミドルウェアが呼び出される
app.shortcut({ callback_id: 'open_modal', type: 'message_action' }, async ({ shortcut, ack, context, client }) => {
try {
// ショートカットリクエストの確認
await ack();

// 組み込みの WebClient を使って views.open API メソッドを呼び出す
const result = await app.client.views.open({
// `context` オブジェクトに保持されたトークンを使用
token: context.botToken,
trigger_id: shortcut.trigger_id,
view: {
type: "modal",
title: {
type: "plain_text",
text: "My App"
},
close: {
type: "plain_text",
text: "Close"
},
blocks: [
{
type: "section",
text: {
type: "mrkdwn",
text: "About the simplest modal you could conceive of :smile:\n\nMaybe <https://api.slack.com/reference/block-kit/interactive-components|*make the modal interactive*> or <https://api.slack.com/surfaces/modals/using#modifying|*learn more advanced modal use cases*>."
}
},
{
type: "context",
elements: [
{
type: "mrkdwn",
text: "Psssst this modal was designed using <https://api.slack.com/tools/block-kit-builder|*Block Kit Builder*>"
}
]
}
]
}
});

console.log(result);
}
catch (error) {
console.error(error);
}
});
```

</details>
2 changes: 1 addition & 1 deletion docs/_basic/listening_responding_shortcuts.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ app.shortcut('open_modal', async ({ shortcut, ack, context, client }) => {

```javascript
// Your middleware will only be called when the callback_id matches 'open_modal' AND the type matches 'message_action'
app.shortcut({ callback_id: 'open_modal', type: 'message_action' }, async ({ action, ack, context, client }) => {
app.shortcut({ callback_id: 'open_modal', type: 'message_action' }, async ({ shortcut, ack, context, client }) => {
try {
// Acknowledge shortcut request
await ack();
Expand Down
2 changes: 1 addition & 1 deletion docs/_tutorials/ja_getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ app.message('hello', async ({ message, say }) => {

### アクションの送信と応答

ボタン、選択メニュー、日付ピッカー、ダイアログ、メッセージショートカットなどの機能を使用するには、インタラクティブ性を有効にする必要があります。イベントと同様に、Slack の URL を指定してアクション ( 「ボタン・クリック」など) を送信する必要があります。
ボタン、選択メニュー、日付ピッカー、ダイアログなどの機能を使用するには、インタラクティブ性を有効にする必要があります。イベントと同様に、Slack の URL を指定してアクション ( 「ボタン・クリック」など) を送信する必要があります。

アプリ設定ページに戻り、左側の **Interactive Components** をクリックします。**Request URL** ボックスがもう 1 つあることがわかります。

Expand Down
11 changes: 7 additions & 4 deletions src/App.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import sinon, { SinonSpy } from 'sinon';
import { assert } from 'chai';
import { Override, mergeOverrides, createFakeLogger, delay } from './test-helpers';
import rewiremock from 'rewiremock';
import { ErrorCode, UnknownError } from './errors';
import { ErrorCode, UnknownError, AuthorizationError } from './errors';
import { Receiver, ReceiverEvent, SayFn, NextMiddleware } from './types';
import { ConversationStore } from './conversation-store';
import { LogLevel } from '@slack/logger';
Expand Down Expand Up @@ -288,11 +288,12 @@ describe('App', () => {
assert.isAtLeast(fakeLogger.warn.callCount, invalidReceiverEvents.length);
});

it('should warn and skip when a receiver event fails authorization', async () => {
it('should warn, send to global error handler, and skip when a receiver event fails authorization', async () => {
// Arrange
const fakeLogger = createFakeLogger();
const fakeMiddleware = sinon.fake(noopMiddleware);
const dummyAuthorizationError = new Error();
const dummyOrigError = new Error('auth failed');
const dummyAuthorizationError = new AuthorizationError('auth failed', dummyOrigError);
const dummyReceiverEvent = createDummyReceiverEvent();
const App = await importApp(); // tslint:disable-line:variable-name

Expand All @@ -309,6 +310,9 @@ describe('App', () => {
// Assert
assert(fakeMiddleware.notCalled);
assert(fakeLogger.warn.called);
assert.instanceOf(fakeErrorHandler.firstCall.args[0], Error);
assert.propertyVal(fakeErrorHandler.firstCall.args[0], 'code', ErrorCode.AuthorizationError);
assert.propertyVal(fakeErrorHandler.firstCall.args[0], 'original', dummyAuthorizationError.original);
});

describe('global middleware', () => {
Expand Down Expand Up @@ -440,7 +444,6 @@ describe('App', () => {
app.error(async (actualError) => {
assert.instanceOf(actualError, UnknownError);
assert.equal(actualError.message, error.message);
await delay(); // Make this async to make sure error handlers can be tested
});

await fakeReceiver.sendEvent(dummyReceiverEvent);
Expand Down
Loading