FirebaseからGoogleSpreadSheetに書き込みをし、その更新をメール通知する

#はじめに
当記事は、FirebaseのCloudFunctionからGoogleSpreadSheetにFirestoreへ新規追加されたデータ内容の書き込みをし、その更新をメール通知する実装についての備忘録です。

CloudFunctionの導入は省略しますが、宜しければ[こちら](https://qiita.com/shotashimura/items/32f71b41ac78745fb82d)を参考にしてください。

##FirebaseからGoogeleSpreadSheetに書き込みを行う

大まかな手順
・FIrebaseの設定
・GoogeleSpreadSheet APIの取得
・CloudFunctionの実行

###FIrebaseの設定
FirebaseからGoogleSpreadSheetに書き込みを許可するために、
幾つかデフォルトの設定から変更をする必要があります。

①Firebaseコンソールから「プロジェクトの設定」（歯車のボタン）をクリック

②「サービスアカウント」のタブをクリック

③その他のサービスアカウントの項にある、Googleサービスアカウントへのリンクをクリック

④名前の項が「App Engine default service account」のタブの、`プロジェクトID@appspot.gserviceaccount.com` をコピー

⑤書き込みたいスプレッドシートを開き、右上の「共有」ボタンをクリックして表示されたダイアログの「ユーザーやグループを追加」に先ほどコピーした、`プロジェクトID@appspot.gserviceaccount.com` を入力

###GoogeleSpreadSheet APIの取得

①GoogleCloudPlatformのナビゲージョンメニュー（左上のハンバーガーボタン）から「APIとサービスをクリック」

②ヘッダーの検索欄から「Google Sheets API」を探し、利用するプロジェクト名と一致しているか確認した上で、APIを有効にします

###CloudFunctionの実行

①プロジェクトの「functions」のディレクトリへ移動
※以上のディレクトリからのみ、CloudFunctionはデプロイ出来ます

②ターミナルで「googleapis」をnpm install

```
$ npm install googleapis
```

②index.jsにコードを書いていく

```index.js
const functions = require("firebase-functions");
//npm installしたgoogleAPIを宣言
const googleAPI = require("googleapis");

exports.appendSpreadSheet = functions
  .region("asia-northeast1")
  //この実装では、Cloud Firestoreの「hoge」というコレクションに値が追加されたら、スプレッドシートに書き込みをします
  .firestore.document("hoge/{hogeId}")
  .onCreate((snap, context) => {
    const foo = snap.data();
    googleAPI.auth
      .getClient({
        scopes: ["https://www.googleapis.com/auth/spreadsheets"],
      })
      .then((auth) => {
        const sheets = google.sheets("v4");
        sheets.spreadsheets.values.append({
          auth,
         //シートのURLの https://docs.google.com/spreadsheets/Idはここの部分/edit#gid=0
          spreadsheetId: "xxxxxxxxx",
          //書き込み先のシート名
          range: "書き込むシートの名前",
          //セル範囲
          //公式を参照→ https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/append?hl=ja#InsertDataOption
          valueInputOption: "RAW",
          insertDataOption: "INSERT_ROWS",
          resource: {
            values: [
              [
                //ドキュメントキー
                hoge.hogeId,
                //書き込みたいドキュメントの中身
                foo.name,
                foo.age,
              ],
            ],
          },
        });
      });
  });
```

以上を記述した後、functionをデプロイすることで、
任意のデータベースに値が追加（あるいは変更などでも実装可能）される度に、スプレッドシートにデータの書き込みがされます。

##メール通知を加える

大まかな手順
・Gmailのセキュリティーレベルを下げる
・nodemailerのnpm install
・環境変数の設定
・CloudFunctionの実行


###Gmailのセキュリティーレベルを下げる

①Googleアカウント内の[安全性の低いアプリのアクセス](https://myaccount.google.com/u/0/lesssecureapps)にて、`安全性の低いアプリの許可:` を`有効`にする。

②メール送受信に利用するGoogleアカウントでログインした上で、[Gmailアカウントへのアクセス](https://accounts.google.com/b/0/DisplayUnlockCaptcha)を`許可`する。

###nodemailerのnpm install
①ターミナルで「nodemailer」をnpm install

```
$ npm install nodemailer
```

###環境変数の設定

①ターミナルで下記コマンドを打つ

```
firebase functions:config:set gmail.email="xxxxxxxxxx@gmail.com" gmail.password="xxxxxxxxxx"
```
以上を設定することで、安全にメールアドレスとパスワードをファイル上で変数定義出来ます。（この例の場合、emailならば、`functions.config().gmail.email`で呼び出し）

###CloudFunctionの実行

①index.jsに追記

```index.js
const functions = require("firebase-functions");
const googleAPI = require("googleapis");
//npm installしたnodemailerと環境変数内のアドレスとパスワードを宣言
const nodemailer = require("nodemailer");
const gmailEmail = functions.config().gmail.email;
const gmailPassword = functions.config().gmail.password;
//送信するGmailアドレスの設定
const mailTransport = nodemailer.createTransport({
  host: "smtp.gmail.com",
  port: 465,
  secure: true,
  auth: {
    user: gmailEmail,
    pass: gmailPassword
  }
});

exports.appendSpreadSheet = functions
  .region("asia-northeast1")
  //この実装では、Cloud Firestoreの「hoge」というコレクションに値が追加されたら、スプレッドシートに書き込みをします
  .firestore.document("hoge/{hogeId}")
  .onCreate((snap, context) => {
    const foo = snap.data();
    googleAPI.auth
      .getClient({
        scopes: ["https://www.googleapis.com/auth/spreadsheets"],
      })
      .then((auth) => {
        const sheets = google.sheets("v4");
        sheets.spreadsheets.values.append({
          auth,
          spreadsheetId: "xxxxxxxxx",
          range: "書き込むシートの名前",
          valueInputOption: "RAW",
          insertDataOption: "INSERT_ROWS",
          resource: {
            values: [
              [
                //ドキュメントキー
                hoge.hogeId,
                //書き込みたいドキュメントの中身
                foo.name,
                foo.age,
              ],
            ],
          },
        });
      }).then(() => {
        let mailOptions = {
          from: gmailEmail,
          to: gmailEmail,
          subject: "メールタイトル",
          text: "メール本文"
        };
        mailTransport.sendMail(mailOptions, (err, info) => {
          if (err) {
            console.error(err);
            return;
          }
          console.log("success");
        });
      });
  });
```

以上、追記した内容をデプロイ後、Firestoreに値が追加される度に、メール通知がされます。
もしメールが届かない場合はCloudFunctionのログを読んだ上で、Googleアカウントのセキュリティのブロックを疑ってみると良いかもしれません。

####おわりに
私が所属するチームは少数なので、作業を自動化することで、ヒューマンエラーを減らしたり、捻出した時間を他の作業に割けるのでとても助かりました。

こちらの記事を参考にさせていただきました。ありがとうございます！
・[スプレッドシートとの連携](https://qiita.com/m_norii/items/63cc8f5eb91a3fc5505f)　
・[メールの送信](https://code.st40.xyz/article/935)　
・[Gmailのセキュリティ](https://stackoverflow.com/questions/51980436/nodemailer-throws-error-invalid-login-534-5-7-14)