CloudfunctionsでGoogle SpreadSheet APIを動かしたい
基本的には以下のGoogle 公式コードを動かすまでを解説
自力でやるには、spreadsheetの認証周りが結構大変なので、サンプルコードを最大限再利用するのがオススメ。
先に手順を説明した後に解説する。
手順編
注1) 操作したいspreadsheetは事前に作成してIDをメモしておく。
注2) firebase CLIは事前にインストールしておく。
1. コードをpullする
$ git clone https://github.com/firebase/functions-samples
2. API設定
- Firebase projectを作成
- このリンクを踏みOAuth2クライアントIDを生成
- ウェブアプリケーション用を選択
https://[プロジェクト名].firebaseapp.com/oauthcallback
を認証済みのリダイレクトURIに登録- 保存
- 表示される
クライアントID
とクライアントシークレット
をメモ
3. cloudfunctionsのコンフィグ設定
$ firebase functions:config:set googleapi.client_id="[クライアントID]" $ firebase functions:config:set googleapi.client_secret="[クライアントシークレット]" $ firebase functions:config:set googleapi.sheet_id="[spreadsheet ID]"
4. デプロイ
$ firebase deploy
5. 認証
https://[プロジェクト名].firebaseapp.com/authgoogleapi
にアクセスするとOAuth認証が実行される。これは一度だけ実行すれば良い。
6. 連携できる!
サンプルコードをそのまま試すには、testsheetwrite()
のエンドポイントにアクセスすると、Firebase Realtime Databaseの変更をフックしてSpreadSheet書き込みが実行される。
解説編
OAuth2
詳細な解説は後日書きたい。大雑把に言うと、あるサービスのリソースをそのサービスのID/credentialを譲渡せずに第三者が利用可能になるよう認可するようなサービス。
最終的に、spreadsheetAPIへのアクセストークンが発行されればゴール。
5.認証 の部分で一度だけ実行すればいいと言うのは、一回認可すればトークンが発行され、以後そのトークンを使ってAPIを利用できるからである。
サンプルコード概説
./firebase.json
地味にここがキモなんですが、しれっと "hosting" : {"rewrites": [ ...
と言うオプションが入っている。
firebase HostingのURL書き換えを利用して、cloud functionのエンドポイントをfirebase Hostingのドメインにしている。
なぜこんなことをするかというと、OAuth時のリダイレクト先のURLが認証済みドメインである必要があるためである。
OAuthの流れとしては、後述の/authgoogleapi
→ /oauthcallback
と言うcloud functionが呼び出される。
/authgoogleapi
でOAuth2認証・認可を行い、spreadsheetAPI用のトークンを取得。そのトークンを保存する処理を/oauthcallback
で実行する。このコールバック処理がどこでも実行されてしまうのは困るわけだから、事前に登録されたドメインのみに制限されている。
cloud functionのデフォルトのエンドポイントは、https://[REGION_NAME]-[PROJECT_NAME].cloudfunctions.net/[FUNC_NAME]
になっており、このドメインはGCPのデフォルトで承認されていないようである。
なのでやり方としては、
.cloudfunctions.net
のドメインをGCP projectに登録.cloudfunctions.net
を登録済みドメインにredirect
があるわけだが、GCPコンソールでの操作が最小限で済む後者をサンプルコードでは採用している。
firebaseapp.comドメインはデフォルトで登録されている(hosting用ドメインだからね...)ので、こちらにURI rewriteを使って転送している。
従って$ firebase deploy
を叩くと、firebase hostingとfirebase functionsの両者がデプロイ対象になる。
./funcitons/index.js
まず実装されている関数を整理すると以下の通りとなる。
exports.authgoogleapi = functions.https.onRequest() exports.oauthcallback = functions.https.onRequest() exports.appendrecordtospreadsheet = functions.database.ref.onCreate() function appendPromise() async function getAuthorizedClient() exports.testsheetwrite()
exportsされている関数はcloud functionで実行管理されるものとなる。
authgoogleapi
とoauthcallback
は、OAuthに使われるエンドポイントである。
appendrecordtospreadsheet
がspreadsheet書き込みの関数になる。サンプルコードではfirebase realtime databaseの要素追加でフックしている。
OAuth用のクライアントIDとクライアントシークレットは、cloudfunctionの環境変数に設定してあるので(config:setのコマンド)、関数実行時に取得できる。
API tokenは初期状態ではもちろん存在しない。OAuth後にoauthcallback
で受け、サンプルではrealtime databaseに保存している。
そして次回実行以降はそこから読み出すようにしている。
処理実行の本体はappendrecordtospreadsheet()
関数である。
spreadsheet APIの実行には、clientインスタンスが必要だが、これをgetAuthorizedClient()
で取得している。
最終的にPromissをreturnしているが、これはcloudfunctionの要件である。
注) ちなみにasyncを使っているので、Nodeのバージョンは8以降でないといけない。
CloudFunctionsでver8以降を使うには、package.jsonに
"engines": { "node": "8" }
を追加しておく必要がある。
要はOAuth2をgoogleapiを使って実装するということである。しかしただでさえfirebase CLIとかspreadsheet APIとかを調べるので大変で、更にgoogleapiでOAuthの部分を調べて実装するコストを考えると、せっかく本家のコードがあるんでこれを丸っと使わない手はないだろうと言う感じで、今回の手法となった。
参考にしたサイト
functions-samples/google-sheet-sync at Node-8 · firebase/functions-samples · GitHub