【Firebase】Cloud Functions for Firebase を使用してスケジューラ関数を定義する

<!-- ==================================================== -->
# 概要
<!-- ==================================================== -->
**Cloud Functions for Firebase**を使用して、任意時間ごとに実行される関数を作成します。

目的としては、Next.jsで作成したサイトが、SSG時にAPI経由でデータを取得しているため、VercelのDeploy Hooksを使って定期的な更新をかけたいと思いました。サイトを更新（リビルド）することで再びSSGされるので、最新のAPIデータがサイトに反映される設計です。

Next.jsのサイト設計に興味のある方は、以下の記事を参考にしてみてください。

https://qiita.com/nemutas/items/523f387877215b0cfc1f

---
スケジューラ関数について、私の環境では**firebase-toolsを使用したdeployがうまくいかず**、少し強引な解決方法を取りました。
<font color="Blue">この記事では、その解決方法をメインにまとめいています。</font>

ただし、明確な解決方法はわかっていません。**こうすれば、一応動くものはできました**という感じです。
もし、こんな面倒な方法を取らなくてもこうすれば解決できるよ！と、解決方法をご存知の方は、是非教えてください。

<!-- ==================================================== -->
# バージョン
<!-- ==================================================== -->
* Node.js － v14.17.1
* npm － 7.6.3
* firebase-tools（firebase） － 9.16.6

<!-- ==================================================== -->
# 関数の作成
<!-- ==================================================== -->
### Firebase 初期設定
まず、以下のことを済ませておきます。

**1. Firebaseプロジェクトの作成**
Cloud Functions for Firebaseを利用するためには、有料プランに切り替える必要があります。
![無題3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/8e27c60b-4a63-60dc-3d52-cdd260582593.png)


**2. firebase-toolsのインストール**

```
npm i -g firebase-tools
```
**3. ローカルプロジェクトの作成・初期化**

```:初期化
firebase init
```
`Functions（Cloud Functions for Firebase）`、`既存のプロジェクト → 1で作成したプロジェクト`、`TypeScript`を選択します。

**4. パッケージ インストール**
作成されたfunctionsフォルダ以下で、パッケージをインストールします。

```:
C:\Users\Projects\deploy-hooks\functions> npm i axios dotenv
```

### コーディング
ドキュメントを参考にコーディングします。

https://firebase.google.com/docs/functions/schedule-functions?hl=ja

```.ts:functions/src/index.ts
import axios from 'axios';
import * as dotenv from 'dotenv';
import * as functions from 'firebase-functions';

dotenv.config()
const DEPLOY_HOOKS_URL = process.env.PORTFOLIO_DEPLOY_HOOKS!

export const VercelDeployHooks = functions
	.region('asia-northeast1')
	.pubsub.schedule('every 24 hours')
	.timeZone('Asia/Tokyo')
	.onRun(async context => {
		functions.logger.info('VercelDeployHooks：onRun！')
		await axios.get(DEPLOY_HOOKS_URL)
		return null
	})
```
- [region](https://cloud.google.com/functions/docs/locations)：日本（東京）
- [schedule](https://cloud.google.com/appengine/docs/standard/python/config/cronref?hl=ja#schedule_format)：1日（24時間）に1回
- [timeZone](https://jp.cybozu.help/general/ja/admin/list_systemadmin/list_localization/timezone.html)：東京時間

### Deploy & Error...
これで、あとはFirebaseにデプロイすれば終わり！

```
npm i deploy
```

のはずでした....
以下のようなエラーが出力され、正常にデプロイできません...:worried:

```
+  functions: Finished running predeploy script.
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
+  functions: required API cloudbuild.googleapis.com is enabled
+  functions: required API cloudfunctions.googleapis.com is enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (49.44 KB) for uploading
i  pubsub: ensuring required API pubsub.googleapis.com is enabled...
i  scheduler: ensuring required API cloudscheduler.googleapis.com is enabled...
+  scheduler: required API cloudscheduler.googleapis.com is enabled
+  pubsub: required API pubsub.googleapis.com is enabled
+  functions: functions folder uploaded successfully
i  functions: creating Node.js 14 function VercelDeployHooks(asia-northeast1)...
+  functions[VercelDeployHooks(asia-northeast1)]: Successful create operation. 
i  functions: cleaning up build files...

Functions deploy had errors with the following functions:
        VercelDeployHooks(asia-northeast1)

To try redeploying those functions, run:
    firebase deploy --only "functions:VercelDeployHooks"

To continue deploying other features (such as database), run:
    firebase deploy --except functions

Error: Functions did not deploy properly.
npm ERR! code 1
npm ERR! path C:\Users\Projects\deploy-hooks\functions
npm ERR! command failed
npm ERR! command C:\WINDOWS\system32\cmd.exe /d /s /c firebase deploy --only functions

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\AppData\Local\npm-cache\_logs\2021-08-29T05_31_18_042Z-debug.log
```

例えば、以下の記事のようなAPI関数の場合、これまでの手順で正常にデプロイできました。でもなぜか、スケジューラ関数ではエラーになります。

https://qiita.com/nemutas/items/bc13a81e49d9c67fa4fd

<!-- ==================================================== -->
# エラーの解決方法
<!-- ==================================================== -->
### 原因の推測
API関数との差異は、スケジューラ関数ではバックグラウンドで**Pub/Sub API**、**Cloud Scheduler API**を使用していることです。
Google Cloud Platformの**APIとサービス**を確認すると、このAPIにうまくアクセスできずにエラーになっているようです。

このあたりの推測で、調べてみると同じようなエラーに直面している方がいました。

https://zenn.dev/tomon9086/articles/4148f4e97277e2

解決方法として、
> Cloud Scheduler にアクセスして手動でPubSubジョブを作ると直る、以降のデプロイも落ちなくなる

とあるので、試してみます。

### Firebase の状況
Firebaseコンソールを確認すると、firebase-toolsを使用したデプロイは失敗しましたが、関数自体のアップロードはできています。ただし、設定した時間になっても動きません。

![無題0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/fafee504-c1ba-90fc-c87b-436723443e47.png)

### Cloud Scheduler の設定
Google Cloud Platformの**Cloud Scheduler**で、ジョブを作成します。

![スケジューラ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/db74c23f-9f84-a009-a54f-2102adc7a05c.png)

<font color="red">名前</font>や<font color="red">説明</font>、<font color="red">メッセージ</font>は任意みたいですが、**index.ts**で記述したものと合わせておきます。
<font color="red">頻度</font>と<font color="red">タイムゾーン</font>は、**index.ts**で記述した内容を無視してこちらの設定が優先されます。
<font color="red">ターゲットタイプ</font>を**Pub/Sub**にして、<font color="red">トピック</font>でデプロイしたスケジューラ関数を選択します。

頻度について、cron形式で設定します。以下を参照してください。

- [cron ジョブ スケジュールの構成](https://cloud.google.com/scheduler/docs/configuring/cron-job-schedules?hl=ja)
- [クーロン(cron)をさわってみるお](https://qiita.com/katsukii/items/d5f90a6e4592d1414f99)

### Cloud Scheduler の動作確認

作成すると、ダッシュボードに登録されます。作成したスケジューラ関数は`1日に1回`実行されるように設定したので、**[今すぐ実行]**を押してテストしてみます。

![無題1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/78c324b4-54ab-e7e2-d6dd-4de918c2bff4.png)

FirebaseコンソールのFunctionsのログをみると、`VercelDeployHooks：onRun！`と表示されています。

```index.ts
functions.logger.info('VercelDeployHooks：onRun！')
```
![無題2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/1fd0de1c-22c6-3958-14e6-1146bda1f857.png)

そして、Vercelのダッシュボードでは、ビルドされているのがわかります。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358498/16018e06-537a-b682-3f65-f96de07fc60c.png)

<!-- ==================================================== -->
# おまけ：有料プランについて
<!-- ==================================================== -->
ドキュメントにもある通り、Cloud Functions for Firebaseは有料プランで使用することができます。また、スケジューラ関数は、アカウントごとに3つまで無料です。

>Firebase プロジェクトでこのソリューションを使用するには、プロジェクトで Blaze 料金プランを利用している必要があります。Blaze プランをまだ利用していない場合は、料金プランをアップグレードしてください。
>
課金は必須ですが、Cloud Scheduler の各ジョブのコストは月額 $0.10（USD）であり、Google アカウントごとに 3 つのジョブを無料で使用できるため、全体的なコストは管理可能であることが期待できます。Blaze の料金計算ツールを使用すると、予想使用量に基づく料金見積もりを作成できます。