通知設計

Room を使用するケースの例

SlackIntegrationService など route から外れた部分で何かしらの操作が行われるとき、そこで発生する可能性のあるエラーを toaster で通知したい

このとき Growi -> Client のレスポンスは Controller によって使われてしまうので、Service で起こったエラーについては SocketIOService を使用してクライアントに通知してあげるしかない

以前までの仕様では、クライアントが生成するランダムな socketClientId を使用して通知を行なっていた。が、それではその socketClientId を取得できない部分では特定のユーザーに通知することができない。

そこで "ユーザーごと" や "ページごと" など 目的別に Room を定義し、サーバーで適切なタイミングで join させることで、

socket.to(目的の room).emit()

として柔軟に通知することができる

Toaster が出るまでの流れ

  1. userA が サーバーに接続 => サーバーで io.room('user:userId').join(); クライアントでio.room(userId).on('notification', (data) => {});で待機
  2. ページを create => userNotificationService.fire()
  3. この fire() で Proxy(without では growi) で 'not_in_channel' エラー発生
  4. Growi が Proxy からのエラーレスポンスを取得
  5. エラーレスポンスから 'not_in_channel' などの必要なデータを抽出して、io.to('user:userId').emit('notification', data) ここにおける userId は Growi の controller で req.user から抽出できるはず
  6. クライアントは、待機していた io.on('slack-integration(仮)', (data) => {}) の中で data を分析し、toaster を出す

.on('notification', (data) => {}) に渡す data の形

interface SocketNotificationData { body: { [key: string]: any, } error: { code: string, data: { [key: string]: any, }, } }

data.error.code'not_in_channel' などが入ってくる

Rooms 設計

  • クライアントごとに room('user:userId')

    • サブパスでのエラー発生時に通知する
  • Page ごとに room('page:pageId')

    • Page が更新される時に他の閲覧者や編集者に更新されたことを通知する

Join/Leave のタイミング

  • room('user:userId')

    • クライアントがサーバーに接続したときに Join
    • クライアントがサーバーから切断されたときに 自動で Leave
  • room('page:pageId')

    • クライアントがページのリンクに移動したときに Join
    • ページを移動するたびにクライアントは再接続されるので その時に自動で Leave

備考

各種 notificationService で SocketIOService を socketClientId なしで使えるようになる