GASを使ったwrikeのwebhook

# はじめに
私の担当するプロジェクトでは、現在タスク管理にwrike、コミュニケーションツールにslackを利用しています。
wrikeには 

- 柔軟なタスク設定
- ガントチャート機能
- 外部連携機能
- 外部API

などが用意されています。

標準で利用できる`Slack対応Wrikeアプリ`を利用すれば

- タスクの追加
- タスクへのコメント

などをslackのチャンネルに投稿することは可能ですが、slack上のメンションとしてユーザーを紐づけることは現在できません。
そこで、

- wrikeからのwebhookリクエストをGoogleAppsScript(GAS)で受け取る
- 受け取ったリクエストから欲しい情報を抜き出す
- 加工した情報をslackに投稿する

の手順でslackユーザー宛にメンションできるようにしました。
今回`wrikeからのwebhookリクエストをGoogleAppsScript(GAS)で受け取る`までにハマりどころがあったので、その辺りまで書いていこうと思います。

# wrikeからのwebhookリクエストをGoogleAppsScript(GAS)で受け取る
やること

- GASにWebhookを用意する
- wrikeにwebhookを登録する
- GASで受け取ったwebhookを処理する

## GASにwebhookを用意する
詳しくわかりやすく書いてくださっている方がいらっしゃるので、
> [Google Spreadsheet を簡易 Webサーバーとして動かして、手軽にWebHookを受け取る方法](https://qiita.com/kunichiko/items/7f64c7c80b44b15371a3)

を参考にGASのwebhookUrlを発行します

## wrikeにwebhookを登録する
[wrikeのAPIドキュメント](https://developers.wrike.com/)を参考にしながら、更新を検知したいプロジェクト（フォルダ）へのwebhookを設定していきます

### まずはアクセストークンの発行
今回はoath2での認証をせずwrikeの設定ページからアクセスキーを発行します。
wrikeから`アプリ＆統合` -> `APIアプリ` を開いて、適当にアプリを登録。
すると画面最下部に`永久アクセス・トークン`の項目が表示されるので、そちらからアクセストークンを発行します
発行したトークンをAuthorizationのBearerTokenに乗せることでwrikeAPIが利用可能になります。
![スクリーンショット 2020-12-14 10.50.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324924/b7f8c4d0-1812-6f2e-72a1-52aaf05a418c.png)

### APIリクエストを送信してみる
APIを送信する場合は、取得したアクセストークンをAuthorizationのBearerTokenに指定します。
curlで送信する場合はこんな感じ

```
curl -X GET -H "Authorization: bearer [アクセストークン]" https://www.wrike.com/api/v4/folders
```
`https://www.wrike.com/api/v4/folders`のGETリクエスト送信に成功した場合、wrike上のデータのjson配列が返却されます

```
例
[
 ...
    {
      "id": "HOGEHOGE", <- このIDがwebhook登録時に必要になる
      "title": "android",
      "childIds": [],
      "scope": "WsFolder",
      "project": {
        "authorId": "HOGEHOGE",
        "ownerIds": [],
        "customStatusId": "HOGEHOGE",
        "createdDate": "2020-11-09T08:09:27Z"
      }
    },
 ...
]
```
wrikeのwebhookに登録する場合、フォルダごとに設定されているidが必要になりますので、監視対象のフォルダのIDをメモしておきましょう。

### webhookUrlを登録する
`wrikeにwebhookを登録する`で用意したGASのwebhookUrlをwrikeに登録していきます。
wrikeのwebhookに関するドキュメントは[こちら](https://developers.wrike.com/webhooks/)
今回はフォルダ（プロジェクト）を指定して情報更新を取得したいので、
`[POST] /folders/{folderId}/webhooks`でwebhookを登録します。
登録前に指定可能なパラメータをざっと確認します。

- hookUrl 
    - Webhookの対象URL  
     今回はここにGASで発行したwebhookUrlを指定します
- secret (optional)
    - 検証用パラメータ  
    今回は利用しません
- events (optional)
    - wrike上でwebhookのトリガーになるイベント  
    Event Typesで`TaskCreated`や`CommentAdded`など様々なイベントが定義されているので、必要なものを指定します。  
特に指定がない場合は指定したフォルダに関する全てのEventでwebhookUrlが起動されます。  
今回の私の用途ではCommentAddedのみを指定しました  
このパラメータは配列で指定するのでevents=[hogehoge,hogehoge]のフォーマットになります
- recursive (optional)
    - フォルダ以下の変更を再帰的に検知するか  
    デフォルトはfalseなので、フォルダ以下全体を検知する場合はrecursive = trueを指定して送信する必要があります。

といったような感じになっています。
私の場合のcurlコマンドはこんなかんじ

```
curl -X POST -H "Authorization: bearer [アクセストークン]" https://www.wrike.com/api/v4/folders/[監視対象のFolderId]/webhooks?hookUrl=[GASで発行したwebhoolUrl]&recursive=true&events=[CommentAdded]
```
リクエストに成功すると、登録したwebhookの情報が返却されます

```
{
    "kind": "webhooks",
    "data": [
        {
            "id": "IEAD74YSHOGEHOGE",
            "accountId": "HOGEHOGE",
            "folderId": "監視対象のFolderId",
            "hookUrl": "GASで発行したwebhoolUrl",
            "events": [
                "CommentAdded"
            ],
            "recursive": true,
            "status": "Active"
        }
    ]
}
```

これでwrikeへのwebhookの登録は完了です。

## GASで受け取ったwebhookを処理する
指定したFolderに変更が入ると、webhookUrlが呼び出されますが、webhookUrl側で適切な処理をする必要があります。

### GASでのwrikeからのリクエスト受け取り
wrikeからのリクエストは、`POST`の形式で送信されるため、GAS側もdoPost()のメソッドを定義して受け取る必要があります。

```
function doPost(e){
  // この関数で受け取る
  // 処理をお好みで
  // Eventに応じて中身が変わるので、eventTypeをチェックして、それぞれ処理を行うのが良さそう
 return HtmlService.createHtmlOutput('200') // この値をreturnしないとwebhookが自動停止される
}
```

受け取る際やGASスクリプトの更新の際は以下に注意してください

- wrikeから送られてくるイベントはjson配列  
  基本的にはイベントごとにwebhookが呼ばれますが、渡されるデータは配列型で渡されます。  
配列に対して処理を行う実装にしておかないと、データの取得に失敗したり取得漏れが発生したりします。
- doPostの処理成功時は`HtmlService.createHtmlOutput('200')`を返却する
  GASのwebhook呼び出しに対するレスポンスはhttpレスポンス的には200で返却されますが、wrikeで期待している返却値はGAS的には`HtmlService.createHtmlOutput('200')`です。
これが返却されなかった場合、wrikeからwebhookUrlが3回呼び出された後webhookが停止されます。
停止された場合、メールで停止された旨がwrikeから送られてくるので、webhookを再開したい場合はPUTリクエストを送信して手動で再開する必要があります。
webhookの生死確認は`GET https://www.wrike.com/api/v4/webhooks`のリクエストで可能です。
止まっていたら`"status": "Suspended"`になります
![スクリーンショット 2020-12-14 11.42.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324924/089aebba-ab3c-560f-e630-6331c8671747.png)

- GASの更新を行なった場合は、新しいProject versionでの公開が必要
GASのwebhookはProjectVersiondで管理されているため、スクリプトを更新しただけでは更新内容が呼び出されたWebhookに対して反映されません。
webhookの処理に対して更新内容を適用したい場合は、Project versionを更新する必要があります。

これでGASでwrikeの更新を受け取って任意の処理を行うことができるようになりました！
データの加工などについては受け取ったイベントの情報から`GET /folders/{folderId}/tasks`などを利用して必要な情報を揃えていくことになります。
こちらについてはまた別の機会に。

# 振り返り
`WrikeはWebhookのエンドポイントに到達できず、中断されました。`と、言われながらの試行錯誤でしたが、最終的にはwebhookを継続して利用できるようになりました。
外部APIが提供されているサービスだと自分好みに色々できて楽しいですね。