[小ネタ]BoltフレームワークでModal Viewのsubmitイベントでackを書く場所をミスってハマった話

2020.04.08

CX事業本部の阿部です。

ドキュメントよく読め案件ではあるんですが、不注意で妙なハマり方をしてしまったのでその内容を共有します。

事象

Modal Viewのサブミットイベント内で、バリデーション後にエラーレスポンスを返してもさも承認かのごとくビューがクローズされてしまう。

なお、バリデーションは正しく判断できてる状態です。

書いてしまったコード(抜粋)

app.view("view_sample", async({ ack, body, view, context }) => {
    ack();

    if(validateFunction(view)) { // 何がしかのバリデーション
        ack({
                "response_action": "errors",    
                "errors": {
                  "error_block": "error message"
                }
        } as any); // 異常系
    } else {
        ack(); // 正常系
        // 後続処理(省略)
    }
});

どこが問題だったか

2行目の ack() です。 サンプルを拡張して処理を書き進めていく時に消し忘れました。

この ack は何をしているのか

Slackアプリの各イベントによって処理側に渡されたイベントを処理側が承認しているかどうか、Slackに通知するための仕組みです。 公式ドキュメントにもある通り、これは3秒以内にSlack側に通知する必要があります。

Bolt入門ガイド - イベントの確認

つまり、私が書いてしまったコードでは、2行目の ack() でサーバー側はSlackにアクションの承認を返しているので、Slackはこの時点でフォームへの入力は完了して処理が進んでいると判断してフォームを閉じています。 5行目以降のエラー処理はその後に処理されているので、どこにも受け取ってもらえないあてのないエラー処理を投げていたことになります。

日本語のドキュメントには「確認」とあって、私にとってはうまく理解が繋がらなかった箇所で、英語のドキュメントを見直して意味がつながりました。

解決

2行目の ack() を削除しましょう。以下のコードで狙った通りに処理されます。

app.view("view_sample", async({ ack, body, view, context }) => {
    if(validateFunction(view)) { // 何がしかのバリデーション
        ack({
                "response_action": "errors",    
                "errors": {
                  "error_block": "error message"
                }
        } as any); // 異常系
    } else {
        ack(); // 正常系
        // 後続処理(省略)
    }
});

ack後のエラーハンドリングについて

先ほどもあげた通り、この承認処理は3秒以内にSlack側に返却する必要があります。 したがって、フォームを承認する場合、フォームのバリデーション、処理の事前チェックが終わった段階で何がしか返却するのがドキュメント上でも書かれている通り推奨となります。

イベント承認後の処理の中での実行時エラーはどう処理すべきでしょうか?

ユーザとのModal Viewを通じたインタラクションは ack を実行する時点で一旦途切れてしまうので、メッセージのポストなども使った別の通知やアクションを考えなければなりません。 処理のパターンを整理して、ユーザに次のアクションをどう促していくかは、その時々の処理になりますが、入力データの保持方法など実装踏まえて検討したいところです。 現状、書きながら模索しているところなので、まとまったら別エントリにしたいと思います。

余談

ちなみに、Modal Viewの入力バリデーションのエラーレスポンス仕様は以下のようになっています。

Using modals in Slack apps | Slack - Displaying errors in views

私のコードでは、エラーのレスポンスを返すときに as any としてますが、現状モーダルのエラーに必要な属性をboltフレームワークのレスポンスタイプが持っていないのでTypeScriptではキャストする必要があります。

この内容については、GitHubのIssueでも上がっています。 おそらく一時的なものかと思われますので、Issueの内容をご確認ください。

まとめ

こういう一見して何も発生しない類の不注意は気づくまでに時間かかりますね。 Slackアプリのイベント処理のモデルを先に理解しておけば、もう少し気づくのが早かったと思います。 やはりドキュメントちゃんと通しで読んでおくのが大事だな、と痛感いたしました。