Calendar Webhook の実装(1) : hkob の雑記録 (510)

はじめに

hkob の雑記録の第510回目(連続83日目)は、昨日設計した Calendar Webhook の実装の第一部を記録しておきます。先に種明かししますが、運用が面倒なことが判明したので 5 年前に使っていた GAS による実装に戻すことにしています。ただ、自分の記録として残しておきたいので、AI とペアプロした実装記録をまとめておきます。長いので、今日は第一部だけ記録します。実装記録は引用の形で、途中で私のコメントを入れます。

第1部: Notion Webhook Worker 作成(Notion Workers テンプレ)

※ moodle webhook と同じ流れ。ここが完了すると、以降の Google 側 watch / 差分取得の受け口(Webhook URL)が確定する。

昨日書いたように OAuth の認証があるので、先に受け口だけ作成しておく必要があります。今日はその受け口を作成するところまでです。

1-0. ローカル準備

  • [x] 作業ディレクトリを作成
    • mkdir -p ~/Dropbox/workers/calendar-webhook
    • cd ~/Dropbox/workers/calendar-webhook
    • 実行ログ:

        mkdir -p ~/Dropbox/workers/calendar-webhook
        hkob@hM5Air ~> cd ~/Dropbox/workers/calendar-webhook
        hkob@hM5Air ~/D/w/calendar-webhook>
      

フォルダは calendar-webhook としました。

  • [x] Workers テンプレを生成
    • ntn workers new .
    • 入力: Initialize a git repository? = yes
    • 入力: Install dependencies? = yes
    • 実行ログ:

        ntn workers new
        ✔ Directory (/Users/hkob/Library/CloudStorage/Dropbox/workers/calendar-webhook) · .
        ✔ Template downloaded
        ✔ Initialize a git repository? · yes
        ✔ Install dependencies? · yes
        ✔ Template files extracted
        ✔ Git repository initialized
        ✔ Dependencies installed with npm
        ✔ Worker project created
      
        Next steps:
      
          1. ntn workers deploy
      

workers new は moodle-webhook と同じです。

1-1. Worker 実装(骨格だけ作る)

  • [ ] src/index.ts に Webhook 受信→(後で)Google 差分取得→Notion Tasks upsert を実装
  • [x] まずは「受信してログが出る」状態まで作る(200/202 を返す)
    • src/index.ts(初期版):
    • npm run build(結果: エラーなし)
      • 実行ログ:

          npm run build
        
          > @notionhq/workers-template@0.0.0 build
          > tsc
        
        import { CapabilityContext, WebhookEvent, Worker } from "@notionhq/workers";
      
        /**
         * Calendar webhook worker (skeleton).
         *
         * - Receives Google Calendar push notifications (headers only)
         * - Logs the notification
         * - (Later) pulls diffs via Google Calendar API and upserts Notion Tasks
         *
         * Note: webhook.execute must return void (or Promise<void>).
         */
        const worker = new Worker();
        export default worker;
      
        type HeaderMap = Record<string, string | undefined>;
      
        function pickGCalHeaders(headers: HeaderMap) {
          const get = (k: string) => headers[k] ?? headers[k.toLowerCase()];
          return {
              channelId: get("X-Goog-Channel-Id"),
              resourceId: get("X-Goog-Resource-Id"),
              resourceState: get("X-Goog-Resource-State"),
              resourceUri: get("X-Goog-Resource-Uri"),
              messageNumber: get("X-Goog-Message-Number"),
          };
        }
      
        worker.webhook("onCalendarNotification", {
          title: "Google Calendar push notification → sync Tasks",
          description:
              "Receives Google Calendar push notifications and triggers a pull-based sync.",
          execute: async (events: WebhookEvent[], _context: CapabilityContext) => {
              for (const ev of events) {
                  const headers = (ev?.headers ?? {}) as HeaderMap;
                  const h = pickGCalHeaders(headers);
                  console.log("[gcal] push", h);
              }
          },
        });
      

まずは Webhook を受け取って、ログを表示するだけの実装です。

1-2. 1回 deploy(workers.json 生成 + Webhook URL 確定)

  • [x] ntn workers deploy
    • 目的: Worker ID を作成して workers.json を生成(env set の前提)
    • 実行ログ:

        ntn workers deploy
        Deploying to workspace: hkob's ambassador workspace
        ✔ Worker name · calendar-webhook
        [build:out] Running npm install...
        [build:out] added 14 packages, and audited 15 packages in 1s
        [build:out] 2 packages are looking for funding
        [build:out] run `npm fund` for details
        [build:out] found 0 vulnerabilities
        [build:out] Running npm run build...
        [build:out] > @notionhq/workers-template@0.0.0 build
        [build:out] > tsc
        [build:out] Running npm install --omit=dev...
        [build:out] up to date, audited 9 packages in 367ms
        [build:out] 2 packages are looking for funding
        [build:out] run `npm fund` for details
        [build:out] found 0 vulnerabilities
        [build:out] Moving package.json...
        [build:out] Moving package-lock.json...
        [build:out] Moving node_modules...
        [build:out] Creating bundle from dist...
        [build:out] Uploading bundle...
        [build:out] Bundle upload complete.
        [build:out] Preparing runtime filesystem for snapshot...
        ✔ Worker created
      
        Webhooks:
        + onCalendarNotification
      
        Webhook URLs:
        → https://www.notion.so/webhooks/worker/.../onCalendarNotification  (onCalendarNotification)
      
        To list webhook URLs later, run: ntn workers webhooks list
      

先に build でエラーがないことを確認したので、deploy もそのまま普通に終わりました。これで、Webhook の URL が確定します。

表示された Webhook URL を記録しています。

1-3. 環境変数(後で埋める欄)

  • [x] TASKS_DATA_SOURCE_ID(Tasks の data source id)
    • 値: 1f70ce2a-6520-80ce-bc36-000b08bc3b11
    • 設定コマンド:
      • ntn workers env set TASKS_DATA_SOURCE_ID=1f70ce2a-6520-80ce-bc36-000b08bc3b11
    • 実行ログ:

        ntn workers env set TASKS_DATA_SOURCE_ID=1f70ce2a-6520-80ce-bc36-000b08bc3b11
        ✔ Set environment variable 'TASKS_DATA_SOURCE_ID’
      

Tasks データベースの data_source_id を環境変数に設定しました。

  • [x] CALENDAR_ID(同期対象カレンダー ID)
    • 値: ....@group.calendar.google.com
    • 設定コマンド:
      • ntn workers env set CALENDAR_ID=....@group.calendar.google.com
    • 実行ログ:

        ntn workers env set CALENDAR_ID=....@group.calendar.google.com
        ✔ Set environment variable 'CALENDAR_ID'
      

同様に Google Calendar の ID も環境変数に登録しました。

  • [ ] (必要なら)署名/認証用の secret(方式確定後)

ここには記録していませんでしたが、別の部分で設定しています。

  • [x] NOTION_API_TOKEN(Notion API token / integration secret)
    • 用途: context.notion.querySql / context.notion.updatePage など、Notion への読み書きを行うために必要
    • 値: (秘匿。ログ/手順書に平文で貼らない)
    • 重要: NOTION_ で始まる環境変数は ntn workers env set では設定できない(reserved prefix)
    • 設定手順(値は貼らない):

      1. .env に追記
        • NOTION_API_TOKEN=...
      2. ntn workers env push
        • 実行ログ:

            ntn workers env push
            The following changes will be pushed to the remote environment:
              + NOTION_API_TOKEN
            ✔ Push changes? · yes
            ✔ Environment variables pushed successfully
          
    • 注意:

      • secret なので、.env は git 管理しない(.gitignore
      • Workers 側の env は Worker 実行時のみ有効(ローカル実行には引き継がれない)

context.notion を使う場合のトークン取り扱いは moodle と同様(reserved prefix 注意)。

Webhook の場合には、context.notion の場合でも internal integration key が必要なことが漏れていたので、追加してもらいました。さらに、NOTION_API_TOKEN は ntn コマンドでは設定できません。前回失敗しているのですが、忘れて ntn コマンドを使ってきたので、moodle webhook の時の話をしたら、修正してくれました。

おわりに

GAS による実装に戻すことにしたので、あくまで参考的な記録となります。とはいえ、書かれたソースコードなどは参考になるものもあるので、爪痕だけは残しておきたいと記録に残すことにしました。

https://hkob.notion.site/hkob-16dd8e4e98ab807cbe3cf3cc94cdfe0f?pvs=4hkob.notion.site