Firestoreの変更をCloudFunctionでTriggerする方法


# ConsoleUIで設定
この画像でTriggerの部分を見てもらうとCloudFirestoreとHTTPがあるのですが、これの設定方法について解説します。
![Screenshot 2022-12-16 at 13.36.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537802/1c49c7fa-712e-eaa0-f20d-086c9b623528.png)

まず「CREATE FUNCTION」を押します。
次に、こういう感じの画面に変わります。
![Screenshot 2022-12-16 at 13.39.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537802/2ba88006-60e1-dc04-6429-e066b8106a8d.png)

# 1st or 2st gen の設定
Environmentは「1st gen」と「2st gen」があります。
2022年12月時点では、Firestoreのトリガーは1genでできるみたいでした。

Functionは、トップ画面で識別するための名前なので、「Function-1」とか適当にいれます。
Regionはサーバーの場所なのかな？Tokyoなので、asia-northeast1とかでいいと思います。

そのしたにTriggerってのがあって、ここがHTTPになっていますが、これを「CloudFirestore」に変えます。

そしたらこんな画面に変わります。
![Screenshot 2022-12-16 at 13.44.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537802/13b6d194-1d10-a6f4-2e98-e2cea2ad7fbb.png)

# EventTypeの設定
で、EventTypeを設定します。
![Screenshot 2022-12-16 at 13.45.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537802/6fedcd47-cc51-ac93-61c2-261769f758bc.png)

簡単にいうと、createが作成時にトリガー、updateが変更時にトリガー、deleteは削除時にトリガー、writeは作成か変更か削除でトリガーです。

# Document Pathの設定
僕がハマったのは、この次のドキュメントPathの書き方です。公式のリファレンスでは、users/marieって書いたら、marieの変更が取れますって書いてるけど、僕がわからなかったのはusersはcollectionなのか、それとも予約語？みたいになりました。そこから色々試すことになりました。色々試してわかったのは、usersがcollection名で、marieはdocument名ってことでした。もし、「students」というcollectionと「bob」というdocument名があるならstudents/bobみたいな書き方になります。

![Screenshot 2022-12-16 at 13.48.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537802/18909df9-832f-b31f-2141-ee7c4c5dc10a.png)

で、ネットで調べるとDocumentの内容の変更は取れるけど、collectionの変更は取れないみたいな記事があったような気がして、少し諦めていたのですが、全然問題なくcollectionにstudentsが追加されたら変更通知を受け取ることができました。
```
documentのbobが変更されたらトリガーされたい場合
students/bob

studentsに生徒が追加されたらトリガーされたい場合
students/{hoge}

(hogeのところはなんでもいいっぽいです)
```

最終的にこんな感じの書き方になります。
![Screenshot 2022-12-16 at 14.05.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/537802/2f7390ce-4f3d-0ebd-91da-4f428b0bf071.png)

これで、ConsoleUIからの設定方法は完了です。

# コード
次にコードの部分です。
トリガーされたらこのメソッドが呼ばれます。呼び出すメソッド名はどこかで指定する場所がありました。そんなに迷うところでもないので、忘れました。

```
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
import json


def mendokusai(data, context):
    trigger_resource = context.resource

    print('Function triggered by change to: %s' % trigger_resource)

    print('\nOld value:')
    print(json.dumps(data["oldValue"]))

    print('\nNew value:')
    print(json.dumps(data["value"]))

    # FirestoreにPythonから追加する時のドキュメントとかに書いてるkeyです。
    key_path = "key.json"
    cred = credentials.Certificate(key_path)
    firebase_admin.initialize_app(cred)
    db = firestore.client()

    params = {}

    # dataは引数から入ってくるdictionaryなんですが、中身の例を載せておきます。
    # json.dumps(data["value"])で、中身をみたら大体わかると思います。

    fields = data["value"]["fields"]
    for key in fields.keys():
        for data_type, val in fields[key].items():
            if data_type == "integerValue":
                params[key] = int(val)
            elif data_type == "stringValue":
                params[key] = val
            else:
                raise f"[ERROR] Unxpected data type {data_type}"

    doc = db.collection(u'students').document(params["id"])
    doc.set(params)
    return 0
```

# Shellからデプロイする方法

Shellから、デプロイする方法について書きます。
ConsoleUIからデプロイするなら不要です。

```
# documentPathの書き方 (document_wildcardの部分をトリガーしたい内容に置き換えます)
projects/my_project_id_11922960/databases/(default)/documents/collection/{document_wildcard}

例えば、collectionの「students」にdocumentが追加されたら通知を取りたい場合はこう書きます
projects/my_project_id_11922960/databases/(default)/documents/students/{student}
```

```
cd ~/MyProject/
gcloud functions deploy students_trigger \
  --entry-point mendokusai \  # メソッド名です
  --runtime python38 \
  --trigger-event "providers/cloud.firestore/eventTypes/document.write" \
  --trigger-resource "projects/surf-69998/databases/(default)/documents/students/{student}" \
  --region asia-northeast1 \
  --allow-unauthenticated
```


# 感想
なんか、英語で検索しても、いまいち情報が少なくて、結局自力で値をはめ込んで、動きを確認するのを繰り返したために時間がかかりました。
FirestoreとcloudFunctionについて詳しい人とか実際のプロジェクトで運用している知り合いを増やしたいです。なんかそういうコミュニティがあるのかな。。
シェルからデプロイする方が、テストしやすいのですが、コードを管理するのが面倒なので、CosoleUIのがいいのかな。
でもバックアップとして、残しておいた方がいいのかも。リポジトリ増えるの面倒なんだけどな。