サービスアカウントによるGoogle Calendar APIへのアクセス

# はじめに
Google Calendarを使ってスケジュール管理Botを開発する場合、そのAPIを使用して、スケジュール情報を取得することができます。基本的にGoogleが提供するAPI(Google Calendar API等)を使用し、情報を取得するにあたり認証が必要になります。
今回は、GoogleCloudで提供しているサービスアカウントというもので認証を行いました。その一連の方法について解説したいと思います。

# サービスアカウントとは（詳しくは[公式](https://cloud.google.com/iam/docs/service-account-overview)）
[公式](https://cloud.google.com/iam/docs/service-account-overview)だと以下のように説明されています。
> A service account is a special kind of account typically used by an application or compute workload, such as a Compute Engine instance, rather than a person. 
(サービス アカウントは、ユーザーではなく、アプリケーションや Compute Engine インスタンスなどのコンピューティング ワークロードで通常使用される特別なアカウントです。)

少し抽象的ですが、この文章の「ユーザーではなく」という点が重要です。
例えば、私たちが何かのサービスにGoogleアカウントでログインするとします。その場合、私たちが作成したGmailアカウントでログインします。そして、このGmailアカウントとは、ユーザーアカウントのことを指します。

対して、サービスアカウントは、ユーザーアカウントとして認証するわけではなく、アプリケーションやサービスがAPIにアクセスするために使用する特別なアカウントということになります。
つまり、サービスアカウントとはアプリケーションがAPIリクエストを代行実行するために使用できるIDもしくはアカウントを指しています。([公式動画](https://www.youtube.com/watch?v=xXk1YlkKW_k)で分かりやすく解説しています)

# 実際にやってみる
Go言語でサービスアカウントによるGoogle Calendar APIにアクセスしようと思います。
## 下準備
1. [Google Cloud Console](https://console.cloud.google.com)でプロジェクトを作成し、Google Calendar APIを有効にする

2. サービスアカウントを作成したプロジェクト内で生成する

3. Google Calendar APIを有効にする  

4. サービスアカウントで認証するための鍵も作成。鍵タイプはJsonを指定

詳しくは以下を参照ください。
参考
[サービスアカウント作成手順](https://clo-support.flight.co.jp/hc/ja/articles/14858881486873-Google-API-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E4%BD%9C%E6%88%90-%E8%A8%AD%E5%AE%9A%E6%89%8B%E9%A0%86)
[(公式)サービスアカウントの作成](https://support.google.com/workspacemigrate/answer/10839762?hl=ja#zippy=%2C%E6%89%8B%E9%A0%86-cloud-console-%E3%81%A7%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9-%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)

## Google Calendarにて、サービスアカウントを共有
サービスアカウントを作成したことで、
``サービスアカウント名@~~-~~~~~.iam.gserviceaccount.com``
のようなアドレスを取得できていると思います。
ブラウザもしくはアプリ経由でGoogle Calendarにアクセスし、
``My calendars`` > ``Settings and sharing``を選択します。

``Shared with``カテゴリの``+Add people and groups``ボタンでサービスアカウントのアドレスを追加します。

## Google Calendar API にアクセスする 
Google Calendar APIにアクセスするためGolangで実装しました。
コードは以下です。
```gc_access.go
package main

import (
	"context"
	"fmt"
	"log"

	"google.golang.org/api/calendar/v3"
	"google.golang.org/api/option"
)

func main() {
	// 認証情報の読み込み
	client, err := getCalendarClient()
	if err != nil {
		log.Fatalf("Failed to create Calendar client: %v", err)
	}

	// 取得するカレンダーID(Gmail)
	calendarID := "普段使用するGmail" // サービスアカウントのアドレスではない点に注意

	// 予定の取得
	events, err := getEvents(client, calendarID)
	if err != nil {
		log.Fatalf("Failed to retrieve events: %v", err)
	}

	// 予定の詳細を出力
	for _, event := range events {
		fmt.Printf("予定: %s\n開始時刻: %s\n詳細: %s\n\n",
			event.Summary, event.Start.DateTime, event.Description)
	}
}

// 認証情報を使ってGoogle Calendarクライアントを取得
func getCalendarClient() (*calendar.Service, error) {
	ctx := context.Background()
	// 認証キーのファイルパス
	credFile := "service-account.json"

	// サービスアカウントの認証情報を取得
	srv, err := calendar.NewService(ctx, option.WithCredentialsFile(credFile))
	if err != nil {
		log.Fatalf("Failed to create Calendar service: %v", err)
	}

	return srv, nil
}

// カレンダーの予定を取得
func getEvents(srv *calendar.Service, calendarID string) ([]*calendar.Event, error) {
	// 今日の日付
	now := "2025-02-06T00:00:00+09:00"
	tomorrow := "2025-02-07T00:00:00+09:00"

	// 予定を取得 (時間範囲を指定)
	events, err := srv.Events.List(calendarID).
		TimeMin(now).
		TimeMax(tomorrow).
		SingleEvents(true).
		OrderBy("startTime").
		Do()
	if err != nil {
		return nil, fmt.Errorf("failed to retrieve events: %v", err)
	}

	return events.Items, nil
}


```
なお、``service-account.json``がサービスアカウントで認証するための鍵になります。
コードについては、コメントで解説している通りです。

結果は以下のとおりです。
```
$ go run gc_access.go
予定: Individual Meeting
開始時刻: 2025-02-06T12:00:00+09:00
詳細: Reporting on research to a professor
```

APIのために使用しているパッケージは
[google.golang.org/api/calendar/v3](https://pkg.go.dev/google.golang.org/api/calendar/v3)
Calendar API へのアクセスを提供

[google.golang.org/api/option](https://pkg.go.dev/google.golang.org/api/option)
Google API クライアントを設定するためのオプションを提供
コード内で使用されている、``option.WithCredentialsFile``については以下のように説明されています。
> WithCredentialsFile returns a ClientOption that authenticates API calls with the given service account or refresh token JSON credentials file.(WithCredentialsFile は、指定されたサービスアカウントまたはリフレッシュトークンの JSON 資格情報ファイルで API 呼び出しを認証する ClientOption を返します)

つまり、サービスアカウントで認証するためのJsonタイプの鍵を使用することで、Google APIに認証するための設定を返してくれます。

# まとめ
今回は、サービスアカウントによるGoogle Calendar APIにアクセスの解説をしました。
解説している記事があまりなかったので、お役に立てていただければ嬉しいです。

最後に、記事が少ないからこそ、普段からドキュメントを読むくせをつけておくのは大事だと思いました。