GCSへのExcelアップロードをトリガーにFirestoreを更新する

#できたこと
FirebaseのFirestoreを使っており、その更新作業を自動でできないか考えた。
Cloud StorageへのExcelアップロードをトリガーにCloud functionsを発火させ、Firestoreの更新が行われる仕組みづくりをしてみた。
意外と面倒だったのでメモメモ。

#コード
##実装全体
これをcloud functionsへデプロイ。
今回はNode.jsで記述。

```js
//もろもろ
const cors = require('cors')({ origin: true });
const path = require('path');
const os = require('os');
const functions = require('firebase-functions')

//必要な情報
const keyFilename = "path/to/keyfile";
const projectId = "YOUR PROJECT ID";

//おまじない
const admin = require('firebase-admin')
admin.initializeApp(functions.config().firebase)
const gcs = require('@google-cloud/storage')({
    projectId,
    keyFilename
});
const bucket = gcs.bucket("YOUR BUCKET NAME");

//ここからが処理内容
exports.helloWORLD = (event, context) => {
  //トリガーの内容はeventかcontextに入る
  const gcsEvent = event;
  const file_name = event.name
  const tempFilePath = path.join(os.tmpdir(), file_name);

  //gcs bucketからexcelをダウンロードする
  //一時ファイルとしてfunctionsのローカル環境にダウンロードする
  bucket
    .file(file_name)
    .download({
      destination: tempFilePath
    })
    .catch(err => {
      console.error('テンプレファイルダウンロードエラー:', err);
    })
    .then(() => {
      //Excelの処理に必要なおまじない
      let XLSX     = require('xlsx')
      let workbook = XLSX.readFile(tempFilePath)
      
      //Excelの取ってきたいデータを参照
      let sheet_name_list = workbook.SheetNames
      let Sheet1          = workbook.Sheets[sheet_name_list[0]]  // 1つめのシートデータを取得します
      let sheet_info_json     = XLSX.utils.sheet_to_json( Sheet1 )   // データをJSONパースします

      //Firestoreにぶち込むための入れ物
      let input_info = []

      //各行のデータが一つずつclに入る。cl[列のタイトル]でデータが取り出せる
      for (let cl of sheet_info_json){
        input_info.push({"name":cl["品目"],"price":cl["値段"]})
      }

      // データベースの参照を作成
      var fireStore = admin.firestore()
      var citiesRef = fireStore.collection(YOUR COLLECTION NAME);
      citiesRef.doc(YOUR DOCUMENT NAME).set({
        data: input_info}
      )
   }) //then
}; //export

```
package.jsonの中身はこちら。

```json
{
  "name": "sample-cloud-storage",
  "version": "0.0.1",
  "dependencies": {
    "@google-cloud/storage": "^1.6.0",
    "cors":"^2.8.5",
    "firebase": "^7.14.2",
    "firebase-admin": "^8.6.0",
    "firebase-functions": "^3.3.0",
    "os":"^0.1.1",
    "path":"^0.12.7",
    "xlsx":"^0.16.9"
  }
}
```

##実装こまごま
###GCS⇔cloud functionsの連携
[こちらの記事](https://qiita.com/fm0707/items/999f5dd094dee54ebbdd)を無茶苦茶参考にしました。
cloud storageにあるExcelを一旦ローカルに保存するという発想もこちらのものです。
GCSのファイナライズをトリガーにしていますが、eventやcontextの中身についてはFirebaseの[公式サイト](https://cloud.google.com/functions/docs/calling/storage)をご覧ください。

pythonで実装された人の記事は[こちら](https://qiita.com/sugusan/items/56d5f76f28c06502a8d6)や[こちらから](https://nishipy.com/archives/765)ご覧ください。
個人的に「バケット名ってどこからどこまでやねん」となりましたが、gs://ultra-soul.appspot.comの場合、ultra-soul.appspot.comがバケット名です。
[公式ドキュメント](https://firebase.google.com/docs/storage/admin/start?hl=ja)に書かれていたけど全然気づかなかった。。。

###cloud functions内のExcelの処理
Excelをいじれるモジュールはいろいろあるみたいで、今回はxlsxを利用しました。
幾つか調べものをした結果をここにも載せておきます。

・exceljsモジュール
　便利そうで実際ハンズオンしたけれど、今回の目的には少し使いにくかった。
　npm公式サイトは[こちらから](https://www.npmjs.com/package/exceljs#rich-text)

・xlsxモジュール
　今回はこちらを利用！参考記事は[こちらから](https://photo-tea.com/p/17/excel-parse-javascript-node/)

ちなみにcsvの場合は[こちらの記事](https://orangelog.site/firebase/firestore-csv-import/)が便利そう。
csvばかりでExcelをインポートするための情報が少なすぎて苦労しました。。。

###cloud functions⇔Firebase firestoreの連携
[こちらの記事](https://qiita.com/kaba/items/35a82b470354b9db29ba)を無茶苦茶参考にしました。
collectionとかdocumentとかの設計については[こちらの記事](https://qiita.com/subaru44k/items/a88e638333b8d5cc29f2)を参考にしました。
collectionは検索するのが難しく、documentは検索がサクッと行えるのが私個人的な実感。
APIドキュメントは[こちらから](https://firebase.google.com/docs/reference/node/firebase.firestore?hl=ja)

###Firebase CLIを使ったデプロイ
これは素直に[公式ドキュメント](https://firebase.google.com/docs/functions/get-started?hl=ja)を参考にしました。
CLI上でプロジェクトの切り替えの方法は[こちらから](https://qiita.com/maatumoDev/items/bcca63276dcae3b22f5b)
使い慣れると便利！！

#最後に
pythonでも実装してみたいなぁ