Firebase Functionsを定期実行する

# はじめに
Firebase Functionsで定期実行をできることを知ったので今回はそれを試してみる。

# TL;DR.
[試したソースコード](https://github.com/Tetsuya-Minase/inform-weather-forecast)
(既存のプロジェクトでやったため、最小構成ではない)

# Firebase(+ GCP)の設定
## プロジェクト作成
UIからポチポチ設定していく。
名前決めるくらいなのでサクッとすすめる。
アナリティクスはいらなかったので無効にする。

<img width="1562" alt="スクリーンショット 2020-03-03 22.28.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176571/18348924-6647-76db-7e92-e0cdcfd464a2.png">
プロジェクト作成できたら左上の歯車からSetting画面を開く。
定期実行する場合`Google Cloud Platform（GCP）リソース ロケーション`を設定する必要があるので、
好きなロケーションを選択する。(今回は`asia-northeast1`(東京)にしておく)

作成したプロジェクトが無料プラン(Spark)だった場合、従量課金制のBlazeにしておく。
(定期実行するのに必要)

## GCPの設定
`Cloud Scheduler API`と`Cloud Pub/Sub API`を有効にする必要がある。
`APIライブラリ`から有効にしておく。

## Firebase CLIの設定

``` sh
# firebaseにログイン
$ firebase login

# firebase functions 初期設定
$ firebase init functions
> 色々聞かれるので好みの設定をする
> .firebasercとfirebase.json、functionsディレクトリが作成されていればOK
```

# デプロイするフォルダを変更する
デフォルトの設定では作成された`functions`ディレクトリにある`package.json`を参照してデプロイされる。
この`package.json`で依存とか解決しているらしい。
※初期設定だと`functions/lib/index.js`がデプロイ先になる。

ただ、今回はすでにあるプロジェクトをデプロイしたかったので、そのへんを変更していく。
(＋階層が1段ずれるのもデプロイのためだけに変更するのも嫌だったので)

変更するには`firebase.json`に`"source": "."`を追加すれば良い。
(今回はカレントディレクトリにしたかったため`.`)
合わせて`predeploy`が`functions`配下の`package.json`を見るようprefixが付いていたので消しておく。

``` json
{
  "functions": {
    "source": ".",
    "predeploy": "npm run build"
  }
}
```
# 定期実行するコードを書いていく
[公式のサンプル](https://firebase.google.com/docs/functions/schedule-functions?hl=ja)を参考にしながら書いていく。

``` typescript
export const sampleJob = functions
  // regionはGCPリソース ロケーションと合わせる
  .region('asia-northeast1')
  // 時間設定。よくあるcronの書き方もできる
  .pubsub.schedule('every day 07:00')
  // 時間指定しているので、一応タイムゾーンを設定しておく
  .timeZone('Asia/Tokyo')
  .onRun(context => {
    // 処理
  }
```

# webpackの設定
webpackでバンドルした結果をデプロイしたかったので設定していく。
多分firebase用の設定はあまりないはず。

``` javascript
const path = require('path');
const nodeExternals = require('webpack-node-externals');

module.exports = {
  mode: 'production',
  entry: path.join(__dirname, '/app.ts'),
  target: 'node',
  module: {
    rules: [
      {
        test: /\.ts$/,
        use: [
          {loader: 'ts-loader'},
          {loader: 'eslint-loader'}
        ],
        exclude: /node_modules/
      },
      {
        test: /\.node$/,
        use: 'node-loader'
      }
    ]
  },
  resolve: {
    extensions: ['.js', '.json', '.ts'],
    modules: [path.join(__dirname, 'src'), 'node_modules']
  },
  output: {
    // これを設定しないとうまくデプロイされなかった
    libraryTarget: 'this',
    path: path.join(__dirname, 'dist'),
    filename: 'app.js'
  },
  externals: [nodeExternals()]
};
```

# 環境変数を設定する
パスワードなどコード上に書きたくないものを環境変数に設定していく。
[公式のサンプル](https://firebase.google.com/docs/functions/config-env?hl=jahttps://firebase.google.com/docs/functions/config-env?hl=ja)を真似てすすめる。

``` sh
# 環境変数にslackのwebhookURLを設定
$ firebase functions:config:set slack.url=https://hooks.slack.com/services/XXX
```
取り出すには下記のようにする。

``` typescript
import { config } from 'firebase-functions';
const slackUrl = config().slack.url;
```

# デプロイする
`firebase deploy —only functions`でOK。
ただし、`deploy complete!`とか言いながら失敗することがあるので、
Webコンソールを確認してデプロイが完了してることを確認した方が良いかも。

# まとめ
Firebase Functionsで定期実行する方法をまとめた。
今まで定期実行したい処理がある時はLambdaを使ってたけど、今後はfirebaseで良さそう。
(個人的にGCPに集約したいだけ。)

# 参考リンク
- [はじめに: 最初の関数の記述とデプロイ | Firebase](https://firebase.google.com/docs/functions/get-started?hl=ja)
- [関数のスケジュール設定 | Firebase](https://firebase.google.com/docs/functions/schedule-functions?hl=ja)
- [環境の構成 | Firebase](https://firebase.google.com/docs/functions/config-env?hl=ja)