Cloud Functions for FirebaseでPubSubをトリガしてみる

# はじめに

[Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)はFirebase向けの[Cloud Functions](https://cloud.google.com/functions/?authuser=0)である。Cloud Funcsionsと同様、現在ベータ版で提供されている。

できること、使い方はほぼCloud Functionsと変わらないように見えるが、[
Google Cloud Functions と Firebase](https://firebase.google.com/docs/functions/functions-and-firebase?authuser=0)を見るかぎりでは、[Firebase Realtime Database](https://firebase.google.com/docs/database/?hl=ja)や、[Firebase Authentication](https://firebase.google.com/docs/auth/?hl=ja)、[Firebase Cloud Firestore](https://firebase.google.com/docs/firestore/?hl=ja)のトリガが追加提供されていたり、「Google Cloud Functions と Firebase SDK for Cloud Functions には、完全な相互運用性があるわけではありません。」などと記述があるのでFirebase向けに最適化されたFuncsionsという位置づけなのだろう。

とりあえず試したいことがあったので、Firebase Functionsで[Cloud PubSub](https://cloud.google.com/pubsub/overview?hl=ja)をトリガしてみる。

# やってみる

[Getstarted](https://firebase.google.com/docs/functions/get-started?authuser=0)と[Cloud Pub/Sub トリガー](https://firebase.google.com/docs/functions/pubsub-events?authuser=0)のページを参考にやってみた。

[Getstarted](https://firebase.google.com/docs/functions/get-started?authuser=0)によると、Cloud Functionsは現時点で`Node v.6.11.5`を実行する。なので、ローカルの環境に6.11.5をインストールしたほうがいい。v8.0.0でdeployしたときはエラーが出たので。

例えば[nodebrew](https://github.com/hokaccha/nodebrew)の場合は以下のようにする。

```
nodebrew install-binary v6.11.5
nodebrew use v6.11.5
```

まず、[Firebase コンソール](https://console.firebase.google.com/)に移動してプロジェクトを作成する。
今回は [Firebase Cloud Firestoreを使ってみる](https://qiita.com/bathtimefish/items/164d2b0cd268f209db9b) のときに使った `btf-test-firestore` が残ってたのでそれを使うことにした。

![スクリーンショット 2018-03-21 11.08.20.png](https://qiita-image-store.s3.amazonaws.com/0/61995/2bd42c88-84b8-dfcc-ef64-67310eb22705.png)

プロジェクトに移動して左側メニューの Functions をクリックしてFuncsionsのダッシュボードに移動する。まだ何もない。

![スクリーンショット 2018-03-21 11.10.32.png](https://qiita-image-store.s3.amazonaws.com/0/61995/656ad7e8-8db0-6b9a-15dc-3184a06f2c4e.png)


ターミナルを開いて、[firebase-tools](https://github.com/firebase/firebase-tools) をグローバルインストールする。

```
npm install -g firebase-tools
```

Firebase CLIでログインを実行する。ブラウザが開くので、ログイン認証をし、CLIに戻って正常にログインされたことを確認する。

```
firebase login
```

適当なディレクトリを作って移動し、Funcsionsのプロジェクトを作成する。
プロジェクト名を聞かれるのでFunctionsを設定したいプロジェクトを指定する。今回は `btf-test-firebase`
言語はJavaScriptかTypeScriptを選べる。今回はJavaScript
ESLintを入れる Yes
npm installを実行する Yes

```
mkdir firebase-functions-pubsub-test && cd $_
firebase init functions
❯ btf-test-firestore (btf-test-firestore) 
❯ JavaScript
? Do you want to use ESLint to catch probable bugs and enforce style? Y
? Do you want to install dependencies with npm now?  Y
```

プロジェクトの設定が完了したら `./functions/index.js`を開いて以下のように更新する。
` functions.pubsub.topic('topic-btf').onPublish()` がPubSubをトリガする関数のようだ。`topic-btf`の部分は後でパブリッシュする任意のトピック名を指定する。

```index.js
const functions = require('firebase-functions');

exports.helloPubSub = functions.pubsub.topic('topic-btf').onPublish(event => {
  const pubSubMessage = event.data;
  // Get the `name` attribute of the PubSub message JSON body.
  let name = null;
  try {
    name = pubSubMessage.json.name;
    console.log(`Name: ${name}`);
  } catch (e) {
    console.error('PubSub message was not JSON', e);
  }
  return 0;
});
```

ちなみに、上記のサンプルコードは[Cloud Pub/Sub トリガー](https://firebase.google.com/docs/functions/pubsub-events?authuser=0)に書いてあるサンプルの行末に `return 0`を加えている。現時点ではこうしないと、Functionsが実行された時に `Function returned undefined, expected Promise or value` という警告がログに表示される。なので[こちら](https://stackoverflow.com/questions/47128440/google-firebase-errorfunction-returned-undefined-expected-promise-or-value?rq=1)などを参考に`return 0`を追加した。
試した感じでは `return true` でも警告が出たので、数値や文字列値じゃないとダメなのかもしれない。

`index.js`が更新できたら、デプロイを実行する

```
firebase deploy --only functions

=== Deploying to 'btf-test-firestore'...

i  deploying functions
Running command: npm --prefix "$RESOURCE_DIR" run lint

> functions@ lint /[USER DIR]/firebase-functions-pubsub-test/functions
> eslint .

✔  functions: Finished running predeploy script.
i  functions: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (42.39 KB) for uploading
✔  functions: functions folder uploaded successfully
i  functions: creating function helloPubSub...
✔  functions[helloPubSub]: Successful create operation. 

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/btf-test-firestore/overview
```

デプロイが成功したらFirebase Functionsのダッシュボードに helloPubSub が表示される。

![スクリーンショット 2018-03-21 11.39.21.png](https://qiita-image-store.s3.amazonaws.com/0/61995/a3203646-a955-dc92-f5ec-1a8fb2fb0252.png)


gcloudコマンドでトピックにパブリッシュする。`topic-btf`は`index.js`で指定したトピック名。`--message`でFunctionsでサブスクライブしたメッセージのJSONを指定する。`--project`でFunctionsを設定したプロジェクトIDを指定する。うまくいくとMessage IDが返ってくる。

```
gcloud pubsub topics publish topic-btf --message '{"name":"BathTimeFish"}' --project btf-test-firestore
messageIds:
- '59076465449285'
```

このコマンド、[Cloud Pub/Sub トリガー](https://firebase.google.com/docs/functions/pubsub-events?authuser=0) では現時点で `gcloud beta pubsub topics publish new_topic 'MyMessage'` と書いてあってちょっと古い。オプションの指定の仕方がbetaのときと変わっているようだ。

Firebase Functions コンソールの ログ タブをクリックして正常に実行されているかを確認する。
ちゃんと`event.data.name`が表示されているようだ。

![スクリーンショット 2018-03-21 11.49.27.png](https://qiita-image-store.s3.amazonaws.com/0/61995/36480e35-6be2-a899-84b9-d4dd30751e28.png)

テストし終わったFunctionを削除する。が、ちょっとめんどい。現時点でFirebase Functionsコンソール上ではFunctionsを削除する機能が提供されておらず、firebase CLIコマンドにも削除するコマンドが存在しない。stackoverflowあたりでも困ってる人を見かける。

結論から言うと、GCPのほうのダッシュボードからなら削除できる。[Firebase Cloud Function の関数を削除する](http://starhoshi.hatenablog.com/entry/2017/11/09/Firebase_Cloud_Function_%E3%81%AE%E9%96%A2%E6%95%B0%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B) というズバリな記事を参考にさせていただき、削除することができた。

まず[GCPプロジェクト ダッシュボード](https://console.cloud.google.com/projectselector/home/dashboard)にアクセスして、選択 をクリックする。

![スクリーンショット 2018-03-21 10.48.01.png](https://qiita-image-store.s3.amazonaws.com/0/61995/cc53d77c-8a1f-c30f-47ec-8e97852438cd.png)

Functionsのあるプロジェクトをクリックする。今回は `btf-test-firestore`

![スクリーンショット 2018-03-21 10.49.02.png](https://qiita-image-store.s3.amazonaws.com/0/61995/1333ebac-1fdb-eddb-8554-4c9a6483e008.png)

プロジェクトのダッシュボードに移動するので リソース ペインの Cloud Functions をクリックする。

![スクリーンショット 2018-03-21 10.49.27.png](https://qiita-image-store.s3.amazonaws.com/0/61995/321b59c5-428b-ae4e-4bc3-ae3422e94cd3.png)

先ほど作成した `helloPubSub` の右側オプションメニューから 削除 をクリックする

![スクリーンショット 2018-03-21 10.49.46.png](https://qiita-image-store.s3.amazonaws.com/0/61995/7cc9f010-a0bd-9a8f-0a73-fb2f2464349e.png)

削除 をクリックする

![スクリーンショット 2018-03-21 10.49.58.png](https://qiita-image-store.s3.amazonaws.com/0/61995/73fb98d8-0fcf-5538-c116-1f3a166782a4.png)

Firebaseのコンソールにもどってみると、`helloPubSub`が消えているのが確認できる。

![スクリーンショット 2018-03-21 10.50.36.png](https://qiita-image-store.s3.amazonaws.com/0/61995/ed531100-f289-82ae-99f7-712a4eca2cbe.png)

# まとめ

そのうちFirebase側からも削除できるようにしてもらいたいが、とりあえず特にハマりどころもなく実行することができた。

これでスケーラビリティ考えずにPubSubできて実験レベルではほぼ無料で考えていいというのはすばらしいと思う。Firebase側にFunctionsがあると[Hosting](https://firebase.google.com/docs/hosting/?authuser=0)や[Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/index?authuser=0)、[Cloud Firestore](https://firebase.google.com/docs/auth/?hl=ja)なんかにつなげる設計も直感的に開発しやすくなる気がする。FirebaseだけでもAWSやAzureでやるようなサーバレスはけっこうできますね。