Google CalendarAPIを学ぶ(Step1:APIの仕様、リソースを理解する)

## はじめに
カレンダーの開発を実施する上で、インターネットの標準仕様に合わせて作るのが良いですね。
カレンダーの標準仕様はRFC5545で定義されています。開発する上で必ず目を通す必要はありますが、RFC5545は抽象的な部分があり、いまいちピンとこない部分があります。RFC5545→実装はなかなか難しいので、今回はRFC5545を実装に落とし込んだGoogleCalendarを元に学んでいきます！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/da130535-2c95-41bb-973d-4b77bfb7cf90.png)

https://tex2e.github.io/rfc-translater/html/rfc5545.html

## Google Calendarシリーズ
[Google CalendarAPIを学ぶ(Step1:APIの仕様、リソースを理解する)](https://qiita.com/koji0705/items/fa683484a1a15517d45b)  
[GoogleCalendar APIを学ぶ(Step2:TBL作成、データ挿入)](https://qiita.com/koji0705/items/f5e59490a093a536c020)  
[GoogleCalendar APIを学ぶ(Step3:カレンダーの取得APIを実装する)](https://qiita.com/koji0705/items/c9301529bbe6fc28bad5)  
[GoogleCalendar APIを学ぶ(Step4:カレンダーに紐づくイベントの一覧取得APIを実装する)](https://qiita.com/koji0705/items/7ed44caecb15e367ee11)

## Google Calendar API
Google Calendar APIの概要をつかみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/7ccdd81b-3cbd-4eb5-9d97-18c67f240458.png)

https://developers.google.com/workspace/calendar/api/guides/overview?hl=ja

→こんだけ書かれていてもよくわからないですね😅 1つづつ理解していきましょう。
まずは用語の意味と関係性を示していきます

### 用語の理解

| 要素            | 説明                                                                                      |
|-----------------|-------------------------------------------------------------------------------------------|
| **ユーザー**   | Google カレンダーを利用する主体。１アカウントにつき１つ。                                 |
| **カレンダーリスト** | ユーザーが左側ペインに登録しているカレンダーの一覧。表示・非表示や色・通知設定などユーザー固有のメタデータを管理。 |
| **カレンダー**  | イベント（予定）を格納するコンテナ。タイムゾーン、説明文、デフォルトの通知設定などを保持。  |
| **イベント**    | 単発予定または繰り返し予定の個別インスタンス。開始・終了時刻、参加者、リマインダーなどを含む。 |
| **設定**        | ユーザー全体の環境設定（週の開始曜日、既定リマインダー方法など）。１ユーザーにつき１件。  |
| **ACL**         | カレンダーへのアクセス権を表すルール（owner／writer／reader）。ユーザーやグループ単位で付与。    |

### 関係性

```mermaid
graph LR
  U[ユーザー] --> CL[カレンダーリスト]
  CL --> C[カレンダー]
  C  --> E[イベント]
  U  --> S[設定]
  U -.-> ACL
  ACL -.-> C
```


| 関係                          | 意味                                             |
|-------------------------------|--------------------------------------------------|
| `ユーザー → カレンダーリスト` | 1 ユーザーが複数のカレンダーリストエントリを持つ     |
| `カレンダーリスト → カレンダー` | 1 エントリは 1 つのカレンダーを参照                  |
| `カレンダー → イベント`        | 1 カレンダーに複数のイベントが属する                 |
| `ユーザー → 設定`              | ユーザーごとに 1 件の設定情報が紐づく（1:1）         |
| `ユーザー -.-> ACL`           | ユーザーやグループに ACL ルールが複数紐づく（点線）  |
| `ACL -.-> カレンダー`         | 各 ACL ルールが対象カレンダーへのアクセス権を定義（点線） |

- 実線 (`-->`) は **所有** や **1:N** の構造的な関係を示します。  
- 点線 (`-.->`) は **アクセス権付与** のような「強制ではないが関連する」関係を示します。  

:::note info
ふわっとした理解ができましたね。次はリソースごとに理解していこうと思います。
:::

## リソースの理解
https://developers.google.com/workspace/calendar/api/v3/reference?hl=ja

### カレンダーのリソース

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/f868fede-2a6c-452a-a728-a070657485b0.png)

```json:
{
  "kind": "calendar#calendar",               // リソース種別: カレンダーリソースを表す固定文字列
  "etag": "\"XpAbC1234yz\"",                // バージョン管理用タグ（例: 変更ごとに自動生成される ETag）
  "id": "primary",                          // カレンダーID（"primary" はメインカレンダー、セカンダリなら "xxx@group.calendar.google.com"）
  "summary": "Team Project Calendar",       // カレンダー名（例: チーム用プロジェクトカレンダー）
  "description": "カンファレンスや締め切りを共有するためのチーム用カレンダーです。",  
                                            // 説明文（例: 用途や対象を記載）
  "location": "Tokyo Headquarters",         // 所在地（フリーフォーム、例: 本社所在地）
  "timeZone": "Asia/Tokyo",                 // IANA タイムゾーン名（例: Asia/Tokyo）
  "conferenceProperties": {
    "allowedConferenceSolutionTypes": [
      "hangoutsMeet",   // Google Meet を有効化
      "eventHangout"    // イベントハングアウト（旧 Hangouts）を有効化
    ]
  }
}
```


| プロパティ                                   | 型／値例                                                   | 説明                                                             |
|----------------------------------------------|------------------------------------------------------------|------------------------------------------------------------------|
| **kind**                                     | `string`<br>`"calendar#calendar"`                          | リソース種別を示す固定文字列                                     |
| **etag**                                     | `string`<br>`"\"XpAbC1234yz\""`                            | 現バージョンを示すエンティティタグ（変更検出・楽観的排他制御用） |
| **id**                                       | `string`<br>`"primary"`                                    | カレンダーの一意識別子（主に API 呼び出し時のキー）               |
| **summary**                                  | `string`<br>`"Team Project Calendar"`                      | カレンダー名（タイトル）                                         |
| **description**                              | `string`<br>`"カンファレンスや締め切りを共有するチーム用カレンダー"` | カレンダーの詳細説明                                               |
| **location**                                 | `string`<br>`"Tokyo Headquarters"`                         | カレンダーの所在地情報（フリーフォーム）                         |
| **timeZone**                                 | `string`<br>`"Asia/Tokyo"`                                 | カレンダーのデフォルトタイムゾーン（IANA 名）                     |
| **conferenceProperties**                     | `object`                                                   | 会議リンク機能に関する設定オブジェクト                             |
| └─ **allowedConferenceSolutionTypes**       | `string[]`<br>`["hangoutsMeet","eventHangout"]`            | 使用を許可する会議ソリューションの種類リスト                     |


https://developers.google.com/workspace/calendar/api/v3/reference/calendars?hl=ja#resource

### カレンダーリストのリソース
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/2dbe41e6-edfb-4fca-8664-bddfe538ec2c.png)

```json:
{
  "kind": "calendar#calendarListEntry",                // リソース種別：カレンダーリストエントリ
  "etag": "\"LmNoP5678qrs\"",                         // バージョン管理用タグ
  "id": "team-shifts@example.com",                     // カレンダーID（グループカレンダーなど）
  "summary": "Team Shifts",                            // カレンダーリスト上の表示名
  "description": "部署の勤務シフトを管理するカレンダー",   // カレンダーの詳細説明
  "location": "Tokyo Office",                          // 場所
  "timeZone": "Asia/Tokyo",                            // タイムゾーン
  "summaryOverride": "My Shifts",                      // ユーザーが上書きしたカレンダー名
  "colorId": "7",                                      // 色ID（colorsリソース参照）
  "backgroundColor": "#9ae2f1",                        // 背景色
  "foregroundColor": "#000000",                        // 文字色
  "hidden": false,                                     // 非表示フラグ
  "selected": true,                                    // 左ペインで選択中かどうか
  "accessRole": "writer",                              // 自分に付与された権限（owner/writer/reader/...）
  "defaultReminders": [
    {
      "method": "popup",                               // リマインダー方法
      "minutes": 30                                    // イベント開始30分前
    }
  ],
  "notificationSettings": {
    "notifications": [
      {
        "type": "eventCreation",                       // イベント作成通知
        "method": "email"                              // 通知方法
      },
      {
        "type": "eventChange",                         // イベント変更通知
        "method": "popup"                              // 通知方法
      }
    ]
  },
  "primary": false,                                    // プライマリカレンダーかどうか
  "deleted": false,                                    // リストエントリが削除済みかどうか
  "conferenceProperties": {
    "allowedConferenceSolutionTypes": [
      "hangoutsMeet",                                  // Google Meet
      "eventHangout"                                   // イベントハングアウト
    ]
  }
}
```

https://developers.google.com/workspace/calendar/api/v3/reference/calendarList?hl=ja

### 設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/f8999e15-0c0a-49f6-9b47-2fcad4e1d0ce.png)

```json:
{
  "kind": "calendar#setting",               // リソース種別: 設定リソースを表す固定文字列  [oai_citation:0‡Google for Developers](https://developers.google.com/workspace/calendar/api/v3/reference/settings)
  "etag": "\"ZmNoEnEtAg0123\"",             // バージョン管理用タグ（例: 設定変更ごとに変わる ETag）  [oai_citation:1‡Google for Developers](https://developers.google.com/workspace/calendar/api/v3/reference/settings)
  "id": "timeZone",                         // 設定キー: ユーザーのタイムゾーンを示す設定 ID
  "value": "Asia/Tokyo"                     // 設定値: IANA タイムゾーン名  [oai_citation:2‡Google for Developers](https://developers.google.com/workspace/calendar/api/v3/reference/settings?utm_source=chatgpt.com)
}
```

| プロパティ | 型／値例                       | 説明                                                         |
|------------|--------------------------------|--------------------------------------------------------------|
| **kind**   | `string`<br>`"calendar#setting"` | リソース種別を示す固定文字列                                 |
| **etag**   | `string`<br>`"\"ZmNoEnEtAg0123\""` | リソースのバージョンを表す ETag。変更検出や楽観的排他制御に利用 |
| **id**     | `string`<br>`"timeZone"`         | 設定項目を識別するキー（例：`autoAddHangouts`、`weekStart`、`timeZone`） |
| **value**  | `string`<br>`"Asia/Tokyo"`       | 設定値。設定 ID によって文字列／数値／JSON 形式などが異なる   |


### ACL
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/3754dd48-0851-470f-a9a0-5d71305e0927.png)

```json:
{
  "kind": "calendar#aclRule",             // リソース種別: ACLルール
  "etag": "\"AbCdEf123456\"",             // バージョン管理用ETag（変更ごとに変わるタグ）
  "id": "rule_123abc",                    // このACLエントリの一意ID
  "scope": {
    "type": "user",                       // 対象タイプ: user (特定ユーザー)
    "value": "alice@example.com"          // 対象ユーザーのメールアドレス
  },
  "role": "writer"                        // 付与する権限: writer (読み書き可)
}
```

| プロパティ       | 型／値例                             | 説明                                              |
|------------------|--------------------------------------|---------------------------------------------------|
| **kind**         | `string`<br>`"calendar#aclRule"`     | リソース種別を示す固定文字列                      |
| **etag**         | `string`<br>`"\"AbCdEf123456\""`     | リソースのバージョンを示す ETag                   |
| **id**           | `string`<br>`"rule_123abc"`          | この ACL ルールの一意識別子                       |
| **scope.type**   | `string`<br>`"user"`                 | ルール適用対象のタイプ（`user`/`group`/`domain`/`default`） |
| **scope.value**  | `string`<br>`"alice@example.com"`    | `user`/`group`/`domain` の場合の識別子            |
| **role**         | `string`<br>`"writer"`               | 付与する権限レベル（`owner`/`writer`/`reader`/…） |

#### ACL Rule の `scope.value` 詳細

`scope.value` は `scope.type` に応じて以下のような形式・意味を持ちます。

| scope.type   | value の形式               | 意味                                    | 例                             |
|--------------|----------------------------|-----------------------------------------|--------------------------------|
| **user**     | メールアドレス文字列       | 特定ユーザーを対象                      | `alice@example.com`            |
| **group**    | Google グループのメール    | 特定グループ（Google グループ）を対象   | `dev-team@example.com`         |
| **domain**   | ドメイン名（ホスト部のみ） | ドメイン内のすべてのユーザーを対象      | `example.com`                  |
| **default**  | ―（省略または空文字）     | 未認証ユーザーを含む全員に公開（公開権）| ※フィールド自体が返却されない |

- **user** / **group**  
  - 値は実際に存在するユーザー／グループのメールアドレスである必要があります。  
  - グループは G Suite／Google Workspace のグループを指します。
- **domain**  
  - 自社ドメイン全体にアクセスを許可したいときに使います。  
  - たとえば `example.com` とすると `@example.com` ユーザー全員が対象です。
- **default**  
  - 認証不要でカレンダーを公開したい（全世界に公開）場合に利用。  
  - `value` フィールドは返却されず、`scope` オブジェクトは `{ "type": "default" }` だけになります。


### イベント

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/db1458ed-f298-43da-9765-8250bfae73e2.png)

```json:
{
  "kind": "calendar#event",                     // リソース種別
  "etag": "\"abcd12345\"",                      // バージョン管理用 ETag
  "id": "evt_12345",                            // イベント ID
  "status": "confirmed",                        // 確定／仮予約／キャンセル
  "htmlLink": "https://www.google.com/calendar/event?eid=evt_12345",  
  "created": "2025-05-17T10:00:00Z",             // 作成日時
  "updated": "2025-05-17T12:00:00Z",             // 更新日時
  "summary": "Project Kickoff",                 // タイトル
  "description": "初回キックオフミーティング",  // 詳細
  "location": "オンライン",                     // 場所
  "colorId": "6",                               // 色 ID
  "creator": {                                  // 作成者情報
    "id": "user_123",
    "email": "alice@example.com",
    "displayName": "Alice",
    "self": false
  },
  "organizer": {                                // 主催者情報
    "email": "alice@example.com",
    "displayName": "Alice",
    "self": true
  },
  "start": {                                    // 開始時刻
    "dateTime": "2025-05-20T09:00:00Z",
    "timeZone": "UTC"
  },
  "end": {                                      // 終了時刻
    "dateTime": "2025-05-20T10:00:00Z",
    "timeZone": "UTC"
  },
  "recurrence": [                               // 繰り返しルール
    "RRULE:FREQ=WEEKLY;BYDAY=MO"
  ],
  "recurringEventId": "evt_master_678",         // シリーズイベントのマスター ID
  "originalStartTime": {                        // 例外インスタンスの元開始時刻
    "dateTime": "2025-05-20T09:00:00Z"
  },
  "transparency": "opaque",                     // ブロック状態
  "visibility": "default",                      // 公開範囲
  "iCalUID": "abcd12345@google.com",            // iCal UID
  "sequence": 2,                                // 更新シーケンス番号
  "attendees": [                                // 出席者一覧
    {
      "email": "bob@example.com",
      "displayName": "Bob",
      "responseStatus": "accepted",
      "optional": false
    }
  ],
  "reminders": {                                // リマインダー設定
    "useDefault": false,
    "overrides": [
      {
        "method": "popup",
        "minutes": 10
      }
    ]
  }
}
```

| プロパティ            | 型／値例                                                                                           | 説明                                                                                           |
|-----------------------|----------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
| **kind**              | `string`<br>`"calendar#event"`                                                                     | リソース種別を示す固定文字列                                                                    |
| **etag**              | `string`<br>`"\"abcd12345\""`                                                                     | リソースのバージョンを示す ETag（変更検出・楽観的排他制御用）                                    |
| **id**                | `string`<br>`"evt_12345"`                                                                          | イベントの一意識別子                                                                            |
| **status**            | `string`<br>`"confirmed"`                                                                          | イベントの状態（`confirmed`/`tentative`/`cancelled`）                                         |
| **htmlLink**          | `string`<br>`"https://www.google.com/calendar/event?eid=evt_12345"`                                | ブラウザで開くイベント詳細ページの URL                                                          |
| **created**           | `string`<br>`"2025-05-17T10:00:00Z"`                                                                | イベント作成日時                                                                                |
| **updated**           | `string`<br>`"2025-05-17T12:00:00Z"`                                                                | 最終更新日時                                                                                    |
| **summary**           | `string`<br>`"Project Kickoff"`                                                                     | イベントタイトル                                                                                |
| **description**       | `string`<br>`"初回キックオフミーティング"`                                                           | イベント詳細                                                                                   |
| **location**          | `string`<br>`"オンライン"`                                                                         | イベント開催場所                                                                                |
| **colorId**           | `string`<br>`"6"`                                                                                  | イベント色の識別子（colors リソース参照）                                                      |
| **creator**           | `object`<br>`{email, displayName, self}`                                                           | 作成者情報（メール・表示名・自己フラグなど）                                                   |
| **organizer**         | `object`<br>`{email, displayName, self}`                                                           | 主催者情報（メール・表示名・自己フラグなど）                                                   |
| **start**             | `object`<br>`{dateTime:"2025-05-20T09:00:00Z", timeZone:"UTC"}`                                     | 開始時刻情報（日時＋タイムゾーン）                                                             |
| **end**               | `object`<br>`{dateTime:"2025-05-20T10:00:00Z", timeZone:"UTC"}`                                     | 終了時刻情報（日時＋タイムゾーン）                                                             |
| **recurrence**        | `string[]`<br>`["RRULE:FREQ=WEEKLY;BYDAY=MO"]`                                                      | 繰り返しルールの配列                                                                           |
| **recurringEventId**  | `string`<br>`"evt_master_678"`                                                                     | 繰り返しマスターイベントの ID                                                                   |
| **originalStartTime** | `object`<br>`{dateTime:"2025-05-20T09:00:00Z"}`                                                     | 例外インスタンスの元の開始時刻                                                                  |
| **transparency**      | `string`<br>`"opaque"`                                                                             | ブロック状態（`opaque`=予定ブロック、`transparent`=空き扱い）                                  |
| **visibility**        | `string`<br>`"default"`                                                                            | 公開範囲（`default`/`public`/`private`/`confidential`）                                        |
| **iCalUID**           | `string`<br>`"abcd12345@google.com"`                                                               | iCalendar 標準の UID                                                                           |
| **sequence**          | `integer`<br>`2`                                                                                   | 更新ごとのシーケンス番号                                                                       |
| **attendees**         | `object[]`<br>`[{email,displayName,responseStatus,optional}]`                                       | 出席者リスト（メール・表示名・応答状態・オプション参加など）                                   |
| **reminders**         | `object`<br>`{useDefault:false,overrides:[{method:"popup",minutes:10}]}`                            | リマインダー設定（既定利用フラグ＋個別オーバーライド設定）                                     |


## イベントリソースの拡張フィールド解説

以下では、`recurrence`、`recurringEventId`、`originalStartTime`、`iCalUID`、`transparency`、`visibility` の6つのフィールドについて詳しく説明します。

---

### 1. recurrence

- **型**: `string[]`
- **例**:`["RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR"]`
- **説明**:  
  iCalendar 標準の繰り返しルールを表す文字列の配列。  
  - `RRULE:`（必須）で繰り返しの頻度や曜日を指定  
  - `EXDATE:` で除外日、`RDATE:` で追加日を指定可能
- **用途**:  
  毎週○曜日や毎月○日などの繰り返し予定を定義し、`events.instances` メソッドで個々のインスタンスを取得する

---

### 2. recurringEventId
- **型**: `string`
- **例**:`"evt_master_678"`

- **説明**:  
  繰り返しシリーズのマスターイベント（元のイベント）の `id`。  
  このフィールドが存在する場合、そのイベントは例外インスタンス（時間変更やキャンセルされた回）であることを示します。

- **用途**:  
  例外インスタンスをマスターイベントと紐づけて管理するために使用します。

---

### 3. originalStartTime

- **型**: `object`
```json
{
  "dateTime": "2025-05-20T09:00:00Z",
  "timeZone": "UTC"
}
```

- **説明**:  
  例外インスタンスの「元々の開始時刻」を保持するオブジェクトです。

- **用途**:  
  変更前の予定日時を把握したいときに使用されます。UI での表示や履歴の記録にも有用です。

---

### 4. iCalUID

- **型**: `string`

```js
"abcd12345@google.com"
```

- **説明**:  
  iCalendar 標準仕様に基づく UID。Google Calendar がイベントを外部カレンダーと同期する際に使用されます。

- **用途**:  
  Outlook や Apple カレンダーなどとのイベント同期の際に一貫性を保つために活用されます。

---

### 5. transparency

- **型**: `string`

```js
"opaque"       // 予定あり（ブロック）
"transparent"  // 空き時間扱い
```

- **説明**:  
  Free/Busy 情報におけるブロック状態の制御設定です。

- **用途**:  
  会議のスケジュール候補を表示する際に、他の予定と重ならない時間帯を探すロジックに使用されます。

---

### 6. visibility

- **型**: `string`

```js
"default"      // カレンダー既定に従う
"public"       // 誰でも詳細を閲覧可
"private"      // 詳細を隠して「予定あり」のみ表示
"confidential" // より制限された公開レベル
```

- **説明**:  
  イベントの公開レベルを制御するための設定です。

- **用途**:  
  他ユーザーにどこまで情報を見せるか（または見せないか）を制御し、機密性を保ちつつ共有できます。


## 最後に
ここまででGoogle Calendarの仕様がわかってきました。次のStepでは、実際にTBLの設計をしていこうと思います