Node-REDでGoogleCloudStorageの更新をいい感じに受け取る

[Node-RED Advent Calendar 2020](https://qiita.com/advent-calendar/2020/node-red)の13日目を担当します、ニアムギです。
今回はGoogleCloudStorageの更新をいい感じに受け取る方法を紹介したいと思います。

# 使うもの

* [node-red-contrib-google-cloud](https://flows.nodered.org/node/node-red-contrib-google-cloud)ノード
* [GoogleCloudStorage](https://cloud.google.com/storage/docs/introduction?hl=ja)
* [GoogleCloudPub/Sub](https://cloud.google.com/pubsub/docs/overview?hl=ja)

## [node-red-contrib-google-cloud](https://flows.nodered.org/node/node-red-contrib-google-cloud)ノードとは？
Node-REDでGoogleCloudPlatformのサービスを使えるようにしてくれる素晴らしいノードです。今回使うGCSやPub/Sub以外にもBigQueryやSpeech to Textなども対応しています。

## [GoogleCloudStorage](https://cloud.google.com/storage/docs/introduction?hl=ja)とは？
そのままですがストレージサーバーです。バケットという箱を用意してそこに画像やテキスト、jsonファイルなどを保存できます。

## [GoogleCloudPub/Sub](https://cloud.google.com/pubsub/docs/overview?hl=ja)とは？
MQTTのようなメッセージングサービスです。パブリッシャーとサブスクライバーそれぞれの役割を設定できます。

# いい感じに受け取る方法

**GoogleCloudStorageが更新されたとき、GoogleCloudPub/Subから更新情報を受け取るようにします。**
例えばGoogleCloudStorageに画像データをアップロードしたことをNode-RED側で知りたいとします。他のサービス(MQTTのブローカーなど)を使って知らせることも出来ますが、アップロード＆アナウンスの2つの処理を実装しなければなりません。
それも良いのですが、GoogleCloudStorageが更新されたアナウンスはGoogleCloudPlatformに任せればセキュリティー面で安心です。ぜひ使っていきましょう。

# 下拵え

Node-REDでGoogleCloudStorage、GoogleCloudPub/Subが使えるように設定していきます。

* GoogleCloudStorageにバケットを用意する
* GoogleCloudStorageの更新情報を送るためのサブスクリプションを用意する
* バケットとサブスクリプションを紐づける
* GoogleCloudStorageとGoogleCloudPub/Subにアクセスできるサービスアカウントを作成する

## GoogleCloudStorageにバケットを用意する

「バケットを作成」から作成できます。今回 **testbucket-hoge** というバケットを用意しました。

![gcpSetting_03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/f0d54720-2a32-afe3-1ea2-3fce06094697.png)

## GoogleCloudStorageの更新情報を送るためのサブスクリプションを用意する
GoogleCloudPub/Subにトピックと、そのトピック内にサブスクリプションを追加します。

「トピックの作成」からトピックを作成できます。今回 **topicGCSPull** というトピックを用意しました。
![gcpSetting_00.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/0a105c3b-9caf-8aa7-5cf1-ab495328e698.png)
トピック"topicGCSPull"内にサブスクリプションを作成します。
![gcpSetting_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/ad53031b-a554-7975-3e68-7fa8ceb6bab9.png)
サブスクリプションの役割は「GoogleCloudStorageの更新を受け取る」ですので、配信タイプを**pull**にします。サブスクリプションIDは**pullTest**としました。
![gcpSetting_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/e805b95d-22df-ec15-f3a6-9c0689208f45.png)

## バケットとサブスクリプションを紐づける
バケット「**testbucket-hoge**」とバケットの更新を受け取れるサブスクリプションが出来ました。しかしどのバケットの更新を受け取るか決めてあげなければいけません。
[Pub/Sub Notifications for Cloud Storage の使用法](https://cloud.google.com/storage/docs/reporting-changes?hl=ja)に従ってバケットとサブスクリプションを紐づけます。gstuilがシンプルで設定しやすいと思います。

```
gsutil notification create -t TOPIC_NAME -f json gs://BUCKET_NAME
```
なので、該当するトピックとバケットをセットします。

```
gsutil notification create -t topicGCSPull -f json gs://testbucket-hoge
```

これでバケットの更新を受け取れるようになりました。

## GoogleCloudStorageとGoogleCloudPub/Subにアクセスできるサービスアカウントを作成する

Node-REDからGoogleCloudStorage・GoogleCloudPub/Subにアクセスするには資格情報(credential)が必要になります。サービスアカウントを作成し、json形式の資格情報ファイルを取得します。

ID -> サービスアカウントを開きます。
![serviceAccount_00.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/b4bcc324-4b02-4d16-5ece-95179fc6a470.png)

GoogleCloudStorageとGoogleCloudPub/Subそれぞれのサービスアカウントを作成します。※ひとつにまとめても構いません。
![serviceAccount_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/60a19255-52b5-b75a-b6c8-e110d869a151.png)
GoogleCloudStorageに関するロール(役割)は"Storageオブジェクト管理者"を選択します。
![serviceAccount_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/10f3bb15-9dbf-1905-900f-87c901547f5c.png)
GoogleCloudPub/Subに関するロール(役割)は"Pub/Sub管理者"を選択します。
![serviceAccount_03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/d3ffac4c-ab79-989c-ac5f-83b677e98978.png)

サービスアカウントを作成したら鍵を生成します。鍵をjson形式で取得し、Node-REDで使用します。※誰にも知られないように大切に保管してください。
![serviceAccount_04.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/a0b110c9-65c1-5408-41e2-0cf84ebc9366.png)
![serviceAccount_05.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/67853ceb-118c-ea1b-727a-f9cb27aec154.png)



# Node-REDで使う

必要な設定は済みましたのでNode-REDでノードを使っていきます。

## セット

"node-red-contrib-google-cloud"を追加します
![nodered_00.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/93c1fb19-18f8-3d1b-96f3-567e9310656d.png)

Pub/Subノードをフローに追加します。
プロパティのCredential情報を新規追加します。
![nodered_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/57acc640-471a-e265-9675-e98f0f83da90.png)
Keyに先ほどのjsonファイルの中身を全てコピペします。
※GoogleCloudStorageノードも同様にCredential情報を新規追加します。
![nodered_01_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/8e051d0e-ab09-09de-2f89-3d497a35c739.png)

またプロパティのSubscriptionに **Pub/Subのサブスクリプション名** を設定します。
![nodered_03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/ef7f6448-26a9-8735-201a-dbaea616c62f.png)
Pub/Subのサブスクリプション名はPub/Subのサブスクリプションの詳細で確認できます。
![nodered_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/008c95a1-6802-772f-ac5a-5ae65ef7d3b0.png)
"Assume JSON"にチェックを入れることでpayloadをjson形式で取得できます。チェックを入れないとバイナリー形式となります。
![nodered_04.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/b44e6790-19d6-0e0e-cf1a-166508b7cbe5.png)

## テスト
GoogleCloudPub/Subノード、GoogleCloudStorageノードが正しく動くかテストしてみます。

### Pub/Subの確認
シンプルにGoogleCloudPub/Subノードとdebugノードだけの組み合わせで確認します。
![test_00_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/45363df6-e3d5-64c0-e3e9-5b74c1fbe2bb.png)

GoogleCloudStorageへ追加するテキストファイルを用意しました。
"test.txt"、中身は"PubSubTest"となります。
![test_00.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/7372f8fd-681d-6ced-0e66-113c795f7339.png)
GoogleCloudStorageのバケットへテキストファイルを追加してみます。
![test_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/5901b349-7b2b-315e-23a7-ca4b5da64299.png)
するとGoogleCloudPub/SubのノードがGoogleCloudStorageの更新を教えてくれました！
![test_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/d6e77553-46b7-5e7c-f0a2-dc0f968d2280.png)
バケットのパスやファイル名、追加・更新・削除など、欲しいと思う情報を受け取れます。受け取れる情報は[Cloud Storage の Pub/Sub 通知](https://cloud.google.com/storage/docs/pubsub-notifications?hl=ja)を参照ください。

### GoogleCloudStorageの確認
GoogleCloudPub/SubからGoogleCloudStorageの更新情報を受け取れたので、GoogleCloudStorageのデータを取得してみます。

GoogleCloudStorageへアクセスするノードを使用します。
![test_05.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/ec9ade7a-afd2-6708-64c7-da37daad5434.png)
GoogleCloudStorageへアクセスするノードへの入力はmsg.filenameに"バケット"＆"ファイル名"、msg.contentTypeに"ファイルの種類"を指定します。
全て **GoogleCloudPub/Subで取得したデータ**から分かっています。 
![test_03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/ee92fe25-4980-17c4-d3b6-1246f8c15a74.png)

GoogleCloudStorageへアクセスするノードからの出力はバイナリーデータです。画像ファイルであればそのまま使えますが、テキストファイルだと文字列のほうがありがたいです。以下のように関数を挟むことでバイナリーデータから文字列へ変換できます。
![test_06.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/1acf3038-f0dc-1b20-d96e-013bcd6b2952.png)
出力結果を見ると、確かにテストで用意したテキストファイルの中身と一致してます！
![test_07.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/d2f9857e-c9cd-f145-5f31-024a94ced455.png)
GoogleCloudPub/Subの通知を受けて、GoogleCloudStorageからデータを取得出来るところまで確認できました。


## 注意点

GoogleCloudStorageの更新を全て通知するので、**削除** の場合も通知が飛んできます。その点を考慮してあげる必要があります。

試しにテキストファイルを削除してみると…
![test_08.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/eb989ab5-4295-6051-528f-f8a21794d926.png)
削除したテキストファイルを取得しようとするのでエラーになります
![test_09.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237144/c356697c-720e-f1f2-9a63-0522da0dc665.png)

# まとめ
GoogleCloudPub/Subを使うことで、GoogleCloudStorageの更新を受け取れる方法について紹介しました。GoogleCloudに通知を任せることで、セキュリティー面や通知がちゃんと届くか気にする必要がないのは大きなメリットだと考えています。

みなさんの何か参考になれば幸いです。
ではでは