Google Calendar API: Push Notifications
特定のCalendar上に予定が追加されたり、予定に変更(削除も含む)があった際に通知を受け取ることができる機能 Push Notification があります。
このAPIを使うと、予定に変更があった際にWebhookにて変更があったことを知ることができますが、Webhookにて通知された内容だけでは「何かの予定に対して変更があった」ことしかわからないので、実用には不十分です。このPush Notification(Events: watch)に加えて、Events: listという予定の一覧を取得するAPIを組み合わせます。Events: list APIにはsyncTokenというパラメータがあり、これが予定の変更を監視する肝となります。1
- Events: watch
- Events: list
監視をする仕組み
詳細は長くなるので、ここではざっくりとしたフローを書いておきます。
Channel の作成
まずは特定のカレンダーを指定してEvents: watchを実行してカレンダーに対する通知を行うChannel(監視対象と通知先の組み合わせ) を作成します。
Webhook
Webhookが呼ばれるタイミングが二種類あり、ヘッダの値で区別します。
- ヘッダ値:
X-Goog-Resource-State=sync-
Channelが作成されたという通知としてWebhookがキックされたので、対象カレンダーに対して一度Events: listを実行して、レスポンスに含まれるnextSyncTokenと、Channelの期限(ヘッダ:X-Goog-Expiration)、監視対象カレンダーのID(ヘッダ:X-Goog-Resource-ID)をカレンダーのIDをキーにして保存します。
-
- ヘッダ値:
X-Goog-Resource-State=exsits- こちらが本筋の「予定に変更があった」という通知なので、保存された
nextSyncTokenを使ってEvents: listを実行して変更があった予定の一覧を取得し、nextSyncTokenを実行結果のものに書き換えます。2
- こちらが本筋の「予定に変更があった」という通知なので、保存された
定期実行処理
またWebhookドリブンなフローとは別に、Channelの期限への対応が必要です。
- 定期的に、保存したカレンダーの期限をチェックし、期限が近いものは
Events: watchを実行して新しいChannelを作成してから、Channel: stopを使って古い方のChannelを削除します。
準備
この機能を利用するには以下の準備が必要です。
- Webhookするための https でアクセスできるホスト
- 自己証明書はダメ
- Google Developer Console上のプロジェクト
- Calendar APIを有効にし、ClientIDを作成する
- Webhookとして利用するホストをGoogle Developer Consoleの API & Auth - Push でドメインのWhite listに登録する3
各APIの詳細
Events: watch
予定を監視したいCalendarのidを指定して、Events: watchを実行すると、監視対象と通知先をセットにしたChannelが作成されます。4
パラメータは以下のとおりです。
-
id: リファレンスにはUUIDを指定するよう書かれていますが、実際には特定の記号文字が制限されているだけなので、人間に読みやすいIDでも構いません。5 -
token: 自由な値が設定でき、省略も可能です。何か設定しておくと、Webhookが呼ばれる場合にもヘッダに含まれます。カレンダーのオーナーアカウント等を入れておくと便利かもしれません。 -
type: "web_hook"固定です。他にどんな種類があるのかはわかりません。 -
expiration: 監視が有効な期間(日時)です。6 -
address: Webhookとしてリクエストを投げてもらうURLを設定します。Developer ConsoleのプロジェクトのPush先としてホワイトリストに追加されていないホストを設定した場合は、401 Unauthorized(詳細なメッセージなし)が返されます。
このAPIを実行したレスポンスの書式はリファレンスにて確認できますが、resourceId 以外は意味がありません。このAPIが成功した場合はWebhookとして登録したURLに対してリクエストが投げられますから、そちらで初期処理すればよいです。
Events: list + syncToken
Events: listはカレンダー内の予定を取得するAPIですが、実行するとレスポンスにnextSyncTokenが返されます。その値を次回のAPI実行時のsyncTokenとして指定することで、そのトークンを取得した時よりも後に変更された内容に絞って取得することができます。
変更内容として予定が削除された場合であっても、その予定のidとstatus: canceledがレスポンスに含まれるので便利です。1
Channel: stop
作成したChannelを削除するAPIで、通知を受け取る必要がなくなったり、期限の更新のために新しいChannelを作成した後に実行します。
-
id: 削除対象のChannelのid7 -
resourceId: 削除対象のChannelに含まれるresouceId
他
watch対象について
watchできる監視対象としてEvents: watchを紹介しましたが、Google Calendarはひとつのアカウントに複数のカレンダー(CalendarList)を作成できます。それ自身の変更を監視したり、カレンダーの設定を変更を監視するwatchなども用意されています。
-
CalendarList: watch -
Acl: watch -
Settings: watch
Google App Engineを使用する場合
→どうやらドメインの所有確認の対象として https://myappid.appspot.com と https をつければ所有確認ができ、appspot.comなドメインもそのまま利用できるようです。*.appspot.com なドメインの所有者であることを証明できないため、SSL for Custom Domainを使用することになります。現在であれば、Google Appsへの登録が必要ということになります(2015/06)。
- Developer Consoleの Compute - App Engine - Settingsで、Custom Domainとして追加する
- admin.google.com の Apps - Additional Google ServicesでAdd servicesをクリックし、該当のGAEアプリケーションのIDを追加する
- admin.google.com のSecurity - SSL for Custom Domainsで証明書を登録し、URLを登録する(SNIで良い)
-
定期起動のポーリングで変更を検知するだけで良い場合は、
Events: list+syncTokenでも十分です。 ↩ ↩2 -
処理中に何かが失敗した場合は Webhook のレスポンスとして500などを返せば、Google Calendar側がexponential backoffを使ったリトライをしてくれます。 ↩
-
認可を求めるアプリケーションのホワイトリストにWebhookとして使用するホストが登録されていなければならない、という制限のため、
Events: watchAPIは「API Explorer(というアプリケーション)に対する認可」の権限では正しく動作しません。 ↩ -
現在、アプリケーションで作成したChannelの一覧を取得することはできません。Channelの一覧を管理する必要がある場合は、watch APIを実行した際のidを保存しておくか、通知が来た時の
X-Goog-Channel-IDを参考にするか、意味のある値にするか、といった管理をしなければなりません。 ↩ -
ただし、有効な
Channelで使用しているidと重複すると400 channelIdNotUniqueが返されます。 ↩ -
省略した場合は1週間、最大だと24日程度が指定できます。大きすぎる値を設定使用すると
400 pushInvalidTtlが返されます。 ↩ -
存在しないidを指定した場合は
404 notFoundが返されます。 ↩