ようへいの日々精進XP

よかろうもん

Google Drive API の Push Notification を利用して Google Drive 上の変更を AWS Lambda で受け取る (試み)

これは

YAMAP エンジニア Advent Calendar 2022 の第 24 日目の記事になる予定です。

qiita.com

経緯

Google Drive の変更をプログラムで受け取れないか調べていたら Google Drive API に Push Notification という機能が提供されていることを知りました。

developers.google.com

この Push Notification (以後、Push 通知) を AWS Lambda の Functions URLs で受け取れるか試してみました。

Google Drive API に Push Notification を理解する

概念図

Push 通知の動作は以下のようなイメージです。

ページトーク

概念図には出てきませんが、ページトークンは Google Drive 全体の変更通知を受け取る際には必要となるパラメータになります。

ページトークンは、Google Drive の変更をどこまで通知しているかを示すマーカーのようなもので、変更一覧を取得リクエストのレスポンスに newStartPageToken が含まれていて、次回、変更一覧を取得する際のリクエストパラメータにセットする必要があります。

チャンネル

チャンネルは、変更通知の送信先 URL (事前に作成する必要がある) をパラメータ (address) の一つとして作成します。今回は、変更通知の送信先 URL を Lambda の Function URL に指定します。

以下、ドキュメントより引用したリクエスト例です。

POST https://www.googleapis.com/drive/v3/changes/watch
Authorization: Bearer auth_token_for_current_user
Content-Type: application/json

{
  "id": "4ba78bf0-6a47-11e2-bcfd-0800200c9a77", // Your channel ID.
  "type": "web_hook",
  "address": "https://mydomain.com/notifications", // Your receiving URL.
  ...
  "token": "target=myApp-myChangesChannelDest", // (Optional) Your channel token.
  "expiration": 1426325213000 // (Optional) Your requested channel expiration time.
}

変更内容の通知

変更内容はリクエストヘッダやリクエストボディに記録されます。

以下、ドキュメントより引用したリクエスト例です。

# ヘッダーに記録されるパターン
POST https://mydomain.com/notifications // Your receiving URL.
Content-Type: application/json; utf-8
Content-Length: 0
X-Goog-Channel-ID: 4ba78bf0-6a47-11e2-bcfd-0800200c9a66
X-Goog-Channel-Token: 398348u3tu83ut8uu38
X-Goog-Channel-Expiration: Tue, 19 Nov 2013 01:13:52 GMT
X-Goog-Resource-ID:  ret08u3rv24htgh289g
X-Goog-Resource-URI: https://www.googleapis.com/drive/v3/files/ret08u3rv24htgh289g
X-Goog-Resource-State:  update
X-Goog-Changed: content,properties
X-Goog-Message-Number: 10

# ボディに記録されるパターン
POST https://mydomain.com/notifications // Your receiving URL.
Content-Type: application/json; utf-8
Content-Length: 118
X-Goog-Channel-ID: 8bd90be9-3a58-3122-ab43-9823188a5b43
X-Goog-Channel-Token: 245t1234tt83trrt333
X-Goog-Channel-Expiration: Tue, 19 Nov 2013 01:13:52 GMT
X-Goog-Resource-ID:  ret987df98743md8g
X-Goog-Resource-URI: https://www.googleapis.com/drive/v3/changes
X-Goog-Resource-State:  changed
X-Goog-Message-Number: 23

{
  "kind": "drive#changes"
}

本題 (変更通知を AWS Lambda で受け取る)

構成

シンプルに下図のような構成を手動で構築しました。

Lambda 関数を作成し、Function URLs を払い出した後に、払い出された URL を利用して、手元の環境から Python スクリプトでチャンネルを作成しています。

コード

一応、チャンネルを作成したり停止するスクリプト (main.py) と Lambda 関数 (handler.py) は、以下のリポジトリにアップしておきました。ちなみに、久しぶりに Python を使ってプログラムを書いてみました。

github.com

以後の実験については、このコードを利用して進めます。

実験

事前の準備

  • Lambda 関数を作成する
    • SSM パラメータストアを利用するので、SSM パラメータストアにアクセス出来る権限を付与する
    • Function URL を払い出しておく
  • SSM パラメータストアにトークンをストア出来るようにする
  • GCP のプロジェクトを作成し、各種設定を行う
    • OAuth の同意画面を作成する
    • OAuth Client ID を払い出す (main.py) で利用する
    • サービスアカウントを払い出す (Lambda 関数) で利用する

チャンネル作成

以下のように main.py を実行してチャンネルを作成します。

$ python main.py create
xxxxxxx-8359-11ed-9dc6-xxxxxxxxxxxxxxxxx # チャンネル ID
xxxxxxxxxxxxxxxxxxxxxxxxxxx # リソース ID

ファイル追加

ファイル (higesan.png) を Google Drive にアップロードしました。

下図のように Lambda 関数にアップロードされたファイル名が記録されました。

ファイル削除

ファイル (higesan.png) を Google Drive から削除してみました。

わかりづらくなったことを反省していますが、削除されたファイル名が記録されました。

ファイルの追加も削除も drive#change になってしまうのが気になりますね。。。

アクセス権の付与や削除

Google Drive 上のとあるフォルダを外部のユーザーに共有してみました。

下図のように通知が飛んできました。どうやら、フォルダやファイルにアクセス権を付与した場合にも通知が飛んできてくれるようです。

チャンネル停止

以下のように main.py を実行してチャンネルを停止します。

$ python main.py ${チャンネル ID} ${リソース ID}

ちなみに、本来であれば、上記を実行することでチャンネルが削除されて通知は止まるはずなんですが、検証でむやみやたらにチャンネルを作ってしまったので、引き続き通知が飛んできてしまう状況です。。。

ということで

Google Drive API の Push 通知を利用して、Google Drive 上の各種変更を AWS Lambda で受け取ってみました。Lambda の Function URL は、ちょっとした Webhook を受け取りたい場合等に便利でした。

尚、Push 通知については、ドキュメント等をちゃんと読み込めていない為、挙動をちゃんと把握しきれてはいませんが、これを利用してやりたいこと (変更が発生したファイルを S3 バケットにバックアップしたり、フォルダやファイルの共有をモニタリング等) が出来そうな気がしましたので、引き続き検証を進めていきたいと思います。

以上、現場からの報告でした。

参考資料