技術探し

JavaScriptを中心に記事を書いていきます :;(∩´﹏`∩);:

GaroonとGoogle Calendarを双方向に同期するアプリケーションを書いた

github.com

一週間前にめっちゃやる気出たので作ってました。

できること

シンプルにGaroonとGoogle Calendarでの予定の追加、更新、削除に関する同期が可能です。

注意点

メンバーは同期されない

garoonとgoogle calendarではメンバーが異なるので、基本的に1人用の設計となっています。
e.g. google calendarで予定を作っても自分しかいない等

削除と更新には制約がある

google calendarではundoができたりするので、影響範囲を小さくするため、自分以外の他のメンバーが入っている予定に対しては、デフォルトでGaroon側の予定を消さない and 更新できない仕様になっています。
もし制約を外したい場合は、.envSAFETYfalseするとすべてのイベントに対してこの制約をなくします。

Garoonからの状態は反映に時間がかかる

中央のgaoogleに対しては、以下のように接続します。

つまり、google calendarから予定を変更した場合はリアルタイムに反映されますが、garoonはcrontabによりキックされAPIを叩くため、指定した時間にしかgoogle calendarに反映されません。

モチベーション

自分の予定を入れるのに、2つのカレンダーに登録するのが面倒くさくなってきたのが主な理由です。(エンジニアっぽい。。)

以前、サイボウズの方で出ているJavaの試したのですが証明書周りで使えなかったと、
サイボウズの方はgaroonからgoogle calendarの一方向なので、google calendarから予定の登録できないのは辛いなって思ったので書きました。

developer.cybozu.io

自分も最初のコミット時には、garoon -> google calenadarの目的でしたが。。。
github.com

アーキテクチャ

構成

  • app
  • mysql
    • metaデータと共有フォーマットな予定データを格納しています
  • batch
    • google calendarEvents:watch 実行とチャンネル作成サーバー

上記をdocker-compose内に入れて動かしています。

                       +-----------------+      +-----------------+
                       |                 |      |                 |
                       |     Garoon      |      | Google Calendar |
                       |                 |      |                 |
                       +----+------------+      +-----+-----+-----+
                            ^                         |     ^
                            |                         |     |
                 fetch      |           webhook       |     | post
                 by crontab |   +---------------------+     | for updating a channel
                            |   |                           |
                            |   |                           |
                       +----+---v--+                  +-----+-----+
                       |           |                  |           |
                       |    app    |                  |   batch   |
                       |           |                  |           |
                       |           |                  |           |
                       +-+--+------+                  +-----+-----+
                         |  ^                               |
                         |  | get                           | insert
insert                   |  | events  +-----------+         | the latest channel information
nextSyncToken and events |  |         |           |         |
                         |  +---------+   mysql   |         |
                         |            |           <---------+
                         +----------->+           |
                                      +-----------+

Google Calendar APIの流れ

Push Notifications  |  Calendar API  |  Google Developers

大きく分けて、2つ仕組みが必要です。

定期的にwebhookを動かすようにするためにgoogle calendar apiを叩くアプリ

webhookのチャンネルには、寿命がありそれが切れると送られなくなります。
なので、チャンネルの寿命が無くなる前に、別チャンネルを発行し、切り替える必要があります。

  1. Events::watch にユニークなチャンネルのIDを付け、POSTします。Events::watch
  2. 使わなくなったチャンネルはChannels::stopを叩き、止めます。Channels::stop

上記の処理を定期的に実行する必要があります。
が、自分の場合、x-goog-channel-idx-goog-resource-id の値を付けても404となってしまいstopできないので、放置しています。。。

webhookを監視するためのアプリ

  1. google calendarに登録した、コールバックのURLが叩かれる
  2. headerに入っているx-goog-resource-stateにはその2つの種類があり、syncexistがある
  3. 前回保存したnextSyncTokenを付け、Events::listを叩く
  4. nextSyncTokenを保存する

2の種類には、以下の種類があります。

  • sync: チャンネルが作られた
  • exist: 予定が操作(作成、更新、削除)された

また、Events::listというAPIは、普通に叩いてもすべてのデータが返ってくるので、前回との差分を出すには、offsetを指定する必要があります。
このAPIを叩くと、headerにnextSyncTokenというoffsetの値が入っているので、次回叩くときにこの値を付与します。

garoogle/google-calendar.js at master · hiroppy/garoogle · GitHub

所感

Google CalandarとGaroonみたいな2区間のプロパティを合わせるのは大変だなって思ったのと、webhookのデバッグ大変すぎる〜〜。

もし、google calendarとgaroonをつなぎたい人は使ってみてください:)

github.com