大量 or 同時に届くWebhookをサーバーレスで手軽に確認する

# 最初にまとめ
- 取りこぼしてもよいので手軽に確認する
 - https://webhook.site/
- 一斉にこないが大量に届く
 - GoogleAppScript
- 確実に手軽に確認する
 - Firebase Realtime Database
- 未確認だけど手軽かも
 - ngrok

# ニーズ
通知用途で大量に通知されるWebhookがpublicなネットワークを通って漏れなく届くか、内容が問題ないか、確認する必要があった。
ただ、いちいちサーバー立てて・・・というのはめんどくさいのでサーバーレスでできないかも検討した。

## 受け取るWebhookの内容
下記のようなbodyが通知されるとする。
`{ "foo": "aaaa", "bar": 1111, "maybeFoo": "AAAA" }`
`{ "foo": "bbbb", "bar": 2222, "maybeBar": "BBBB" }`
maybeFooとmaybeBarはOptional。

# webhook.site
お手軽簡単。
URLにアクセスするだけでWebhook受け取りのためのURLが発行され、通知の内容を表示するViewer付き。
ただし、しばらく使ってると通知が届かなくなったり、同時に受け取ったWebhookを取りこぼすことがあるので、漏れなく届くかどうかの確認には使えなかった。

# GoogleAppScript
Webhookは単なるPOSTリクエストなので、もしかしたらGASであれば取りこぼし無くいけるかも。
しかも、スプレッドシートと連携してるし、データ整理もしやすいかも。
と思いチャレンジ。

## 単純なPOSTリクエストの受け取り
まずは単純にPOSTリクエストを受け取って、Jsonをparseして、該当の値をシートに保存する。

```javascript:simpleWebhookLog.gs
function doPost(e) {
  if (e == null || e.postData == null || e.postData.contents == null) {
    return;
  }
  doSomething(e.postData.contents);
  return "OK";
}

function doSomething(contents) {
  var reqBody = JSON.parse(contents);
  
  var ss = SpreadsheetApp.getActive();
  var sheet = ss.getActiveSheet();
  
  var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
  
  var values = [];
  for (i in headers) {
    var header = headers[i];
    var val = "";
    switch(header) {
      case "Date":
        val = new Date();
        break;
      case "foo":
        val = reqBody["foo"];
        break;
      case "bar":
        val = reqBody["bar"];
        break;
      case "maybeFoo":
        if (reqBody["maybeFoo"] != undefined) {
          val = reqBody["maybeFoo"];
        }
        break;
      case "maybeBar":
        if (reqBody["maybeBar"] != undefined) {
          val = reqBody["maybeBar"];
        }
        break;
    }
    values.push(val);
  }
  
  sheet.appendRow(values);
}
```

結果としては、ほどほどに受け取れたが、やはり大量に同時に送ると取りこぼしが発生する。

## 非同期でキャッシュしながら受け取る
下記記事を見つけたので、そのままパクらせていただきました。
[Webhookで起動したGAS(Google AppsScript)の応答を非同期処理で高速化する](https://qiita.com/zensai3805/items/1dc8e6f1be0499add5a6)
`doSomething(a)`の代わりに、`addJobQueue(a)`を呼び出して、
`addJobQueue(a, b)`と`timeDrivenFunction()`を少し簡略化して下記のようにしてみた。

```javascript:asyncWebhookLog.gs
function addJobQueue(contents) {
  cache = CacheService.getScriptCache();
  var data = cache.get("key");
  
  if (data == null) {
    data = [];
  } else {
    data = data.split(';');
  }
  
  data.push(contents);
  cache.put("key", data.join(';'), 60*5);
  
  return;
}

function timeDrivenFunction() {
  cache = CacheService.getScriptCache();
  var data = cache.get("key");
  cache.remove("key");
  
  if (data == null) {
    return;
  } else {
    data = data.split(';');
  }
  
  for (var i = 0; i < data.length; i++) {
    doSomething(data[i]);
  }
  return;
}
```

これを非同期実行して、テスト！
結果としては、先程よりも漏れなく受け取れるようになったが、やはり受け取り漏れが発生した。
これがGASの限界か・・・！！！

# ngrok
手段を探しているうちにngrokを見つけたが、セキュリティ的に社内から使うのが少し怖かったので却下。

# Firebase Realtime Database
またまた調べているうちに、どうやらRealtime Databaseは直接RESTを叩くことで、リクエスト内容を直接保存することができるらしい。
ということで下記を参考にチャレンジ！[^1]
[^1]: もしかしたら方法として古い可能性があることと、シークレットを使ってるのでセキュリティ的に注意したほうがいいかも。
[Firebase Realtime Database を Rest API と Golang でいじってみる](https://qiita.com/tfutada/items/5e80230852db81f23e0e)

Firebaseコンソールから新しいプロジェクトを作って、`Database -> Realtime Database`からRESTのエンドポイントとなるURLをコピー。
![endpoint.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223153/ba752ef9-e586-ea48-09d5-25f29706e536.png)

さらに下記画面からシークレットを取得。
![secret.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223153/8d6d0db1-ee80-6cbe-24c7-23d74450eec0.png)

ここからリクエスト用URLを作成。例えばこんな感じ。
`https://test-app-75f30.firebaseio.com/message.json?auth=ここをsecretにする`

これでWebhookを送ると・・・。できた！!
![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223153/226ca173-b75f-9acc-cab6-d2940620b8ad.png)

大量送信 and 同時送信でも耐えうるので、問題なし！
気をつけることは、使いすぎると料金がかかってしまいそうなので、テストが終わったらWebhookの通知先を変えたり、定期的にデータを削除したりしておく。

###続き
[ElmでView書いてみたよ編](https://qiita.com/niso1985/items/825fd4adaf636f10b38c)