Firebase RemoteConfigが更新されたときにSlackに通知を送る with Cloud Functions for Firebase

<img width="362" alt="スクリーンショット 2019-03-17 18.26.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/126441/f2f4483e-8aed-5130-5345-b3c5eee9db5e.png">

# :pencil: やりたいこと

- Firebase RemoteConfigが更新されたときにSlackの好きなチャンネルに通知する仕組みを作る

# 準備
## 事前知識
### 今回使うサービス

- Cloud Functions for Firebase
    - https://firebase.google.com/docs/functions/?hl=ja
    -  Firebaseの機能やHTTPSリクエストなどのイベントをトリガーとしてバックエンドコードを自動的に実行できるサービス
    - 今回はFirebase RemoteConfigの更新をトリガーとしてSlackにメッセージをPOSTする仕組みを作る
        - トリガー https://firebase.google.com/docs/functions/rc-events?hl=ja

## :computer:  環境構築
- 前提
    - Node.js環境
    - Firebase CLIのインストール
        - `npm install -g firebase-tools`

### Firebase SDK for Cloud Functionsを初期化する
- `firebase login`
    - ログインする

```
firebase login
? Allow Firebase to collect anonymous CLI usage and error reporting information? Yes

Visit this URL on any device to log in:
https://accounts.google.com/o/oauth2/auth?==HOGEHOGE

Waiting for authentication...

✔  Success! Logged in as ログインしたメールアドレス
```

- `firebase init functions`
  - CLI上で対話形式でプロジェクトを設定していく
      - デフォルトで使用するfirebaseプロジェクトを選択
      - 途中言語が選べるので `TypeScript` を選択
      - あとは基本 `Enter` でOK
      - 👇のようなプロジェクトができる

```
myproject
 +- .firebaserc    # Hidden file that helps you quickly switch between
 |
 +- firebase.json  # Describes properties for your project
 |
 +- functions/     # Directory containing all your functions code
      |
      +- package.json  # npm package file describing your Cloud Functions code.
      |
      +- tsconfig.json # Config file containing compiler options.
      |
      +- tslint.json   # Optional file containing rules for TypeScript linting.
      |
      +- src/     # Directory containing TypeScript source
      |   |
      |   +- index.ts     # main source file for your Cloud Functions code
      |
      +- lib/
          |
          +- index.js     # JavaScript output from TypeScript source.
          |
          +- index.js.map # Sourcemap file for TypeScript source.
```

### :arrow_double_down: 便利なnpmパッケージをインストールする
- `cd functions`
- `npm install typed-slack`
    - https://github.com/starhoshi/typed-slack
- `npm install request-promise`
- `npm install @types/request-promise`

### :fire: Firebaseの課金プランを変更

<img width="258" alt="スクリーンショット 2019-03-17 18.24.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/126441/2182afdb-6343-bf74-6f79-44517d6dfeeb.png">


- Firebaseの左下にあるプランを `Blaze` に変更しておきます
    - ※無料プランの場合、CloudFunctionsでHTTP/S通信ができない。


## 🛠実装

1. 環境変数を設定
2. Slack投稿用の`function`を作成
3. RemoteConfigの更新をトリガーとしてSlackに投稿する`function`を作成
4. デプロイ

SlackのWebhookURLをfirebaseの環境変数にセットする

```
firebase functions:config:set slack.webhook_url="https://hooks.slack.com/services/XXXXXX/XXXXXX/XXXXXX"
```

Slack投稿用のfunctionを作成

```src/functions/slack.ts
import * as functions from 'firebase-functions';
import * as Slack from 'typed-slack'

export function postToSlack(sendText: string, username: string, icon_emoji: string, channel: string) {
    const webhookURL = functions.config().slack.webhook_url
    const slack = new Slack.IncomingWebhook(webhookURL)

    slack.send({
        text: sendText,
        username: username,
        icon_emoji: icon_emoji,
        channel: channel
    }).catch(e => {
        console.error(e)
    })
}
```

RemoteConfigの更新をトリガーにしてSlackにカスタムテキストを投稿する`function`を作成

```src/functions/notifyUpdate.ts

import * as functions from 'firebase-functions';
import * as slack from './slack';

export const notifyUpdateRemoteConfigToSlack = functions.remoteConfig.onUpdate(versionMetaData => {
    const currentVersion = versionMetaData.versionNumber;
    const updatedUsername = versionMetaData.updateUser.name;
    const updatedUserEmail = versionMetaData.updateUser.email;
    const message = `RemoteConfig has updated! to v${currentVersion} by ${updatedUsername}(${updatedUserEmail})`;
    slack.postToSlack(message, 'リモコン更新検知マン', ':female-police-officer::skin-tone-2:', '#notify');
})
```

- 最後に`index`を張る

```src/index.ts
import { notifyUpdateRemoteConfigToSlack } from './functions/notifyUpdate';

export {
    notifyUpdateRemoteConfigToSlack,
}
```

- `firebase deploy` でデプロイする

```
...
✔  functions: functions folder uploaded successfully
i  functions: creating Node.js 6 function notifyUpdateRemoteConfigToSlack(us-central1)...
✔  functions[notifyUpdateRemoteConfigToSlack(us-central1)]: Successful create operation.

✔  Deploy complete!
...
```

こんな感じに出たらデプロイ成功 :rocket: 

## :eyes: 動作確認

- プロジェクト内のRemoteConfigをなんでもいいので更新する
- 通知がくれば成功！ :tada:

<img width="362" alt="スクリーンショット 2019-03-17 18.26.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/126441/aa10c33b-e1fd-5448-5941-70aaacc34e9b.png">

## :question: FAQ

- Slackに通知が来ない！
    - Firebaseのプランを `Blaze` にしていますか？
        - `Spark` プラン（デフォルト）の場合、Firebase Cloud Functionsではインターネット通信ができないのでアップグレードする必要があります
    - SlackWebHookのURLは正しいですか？
    - 環境変数を設定していますか？
    - この情報が古い可能性があります
        - RemoteConfigの更新トリガーが変わった、廃止された