Google Calendar APIで予定のCRUD操作を行う

# 概要

Google Calendar APIを用いて、カレンダーの予定についてCRUD操作（作成、取得、更新、削除）を行います。
今回はコードにおける認証部分を楽にしたかったので、サービスアカウントを用いた認証を用いています。

# 環境

Ruby 2.7.1
※今回はRubyを利用していますが、公式ドキュメント中に他の言語の使用例も実例付きで載っていました。

# APIを使用するための下準備

参考文献に記載された手順が大変参考になりました。
[[Ruby]Google Calendar APIから日程を取得しよう](https://qiita.com/flmil/items/bf7ed4b357f3576cf4da)
[Google Calendar APIを使用してみる #1](https://qiita.com/GodPhwng/items/4310586e1ec3bda4d91f)
[Using OAuth 2.0 for Server to Server Applications](https://developers.google.com/identity/protocols/oauth2/service-account)

大きな流れとしては以下のとおりです。

* APIを使うためのプロジェクトを登録し、Google Calendar APIの利用を許可しておく
* 認証方法としてサービスアカウントを登録する
* サービスアカウントに鍵を追加し、JSONファイルをダウンロードしておく
* CRUD操作を行いたいカレンダーの権限をサービスアカウントのメールアドレスに付与する
* CRUD操作を行いたいカレンダーのIDを控えておく

オプションを付与する必要もなく、参考文献を見て画面の指示に従えば登録できると思います。
必要があれば追記します。

# 実装

app.rbにコードを記述していきます。
先程ダウンロードしたJSONファイルはアクセスできる場所に置いておきましょう。

事前にRuby用のAPIクライアントをインストールしましょう。
以下のコマンドをコンソール上で実行しておきます。
```gem install google-api-client```

コードの記述はこちらの記事を参考にしました。
[[Ruby]Google Calendar APIから日程を取得しよう](https://qiita.com/flmil/items/bf7ed4b357f3576cf4da)
それではコードを見ていきましょう。

```app.rb
require "google/apis/calendar_v3"
require "googleauth"
require "googleauth/stores/file_token_store"
require "fileutils"

APPLICATION_NAME = 'TEST_ACCOUNT'
MY_CALENDAR_ID = '操作したいカレンダーのID'
CLIENT_SECRET_PATH = 'JSONファイルのパス'
TIME_ZONE = 'Japan'

class Calendar
  def initialize
    # カレンダー操作用のインスタンスを生成、@serviceとして投入
    @service = Google::Apis::CalendarV3::CalendarService.new
    # オプションとしてアプリケーションの名前を設定（ここではサービスアカウント名）
    @service.client_options.application_name = APPLICATION_NAME
    # 認証を行う
    @service.authorization = authorize
    # 利用するカレンダーのIDを設定する
    @calendar_id = MY_CALENDAR_ID
  end

  def authorize
    # 認証用の情報を格納する
    authorizer = Google::Auth::ServiceAccountCredentials.make_creds(
      json_key_io: File.open(CLIENT_SECRET_PATH),
      scope: Google::Apis::CalendarV3::AUTH_CALENDAR)
    # アクセストークンを持ってくる（失敗したらエラー？）
    authorizer.fetch_access_token!
    # authorizerをリターン
    authorizer
  end

  #レスポンスとして受け取るEventをコンソールに表示
  def puts_event(event)
    puts "Summary:  #{event.summary}"
    puts "Location: #{event.location}"
    puts "ID:       #{event.id}"
    puts "Start:    #{event.start.date_time}"
    puts "End:      #{event.end.date_time}"
  end

  #必要な情報をEventクラスのインスタンスとして格納
  def set_event(summary, description, location, start_time, end_time)
    Google::Apis::CalendarV3::Event.new({
        summary: summary,
        description: description,
        location: location,
        start: Google::Apis::CalendarV3::EventDateTime.new(
          date_time: start_time
        ),
        end: Google::Apis::CalendarV3::EventDateTime.new(
          date_time: end_time
        )
      }
    )
  end

  # 新しい予定の作成
  def create
    #イベントを作成
    event = set_event(
      'inserted test event',
      'test event',
      'test',
      DateTime.new(2020, 8, 23, 12),
      DateTime.new(2020, 8, 23, 15)
    )

    #作成のリクエストを送信し、レスポンスを受け取る
    response =  @service.insert_event(
      @calendar_id,  #calendarID, 必須
      event #挿入したいイベント(Google::Apis::CalendarV3::Event)
    )

    puts_event(response)
  end

  #期間を指定してEventのリストを取得
  def read
    # 2020年の1月から12月1日までの予定を取ってくる
    events = @service.list_events(@calendar_id,
                                  time_min: (Time.new(2020, 1, 1)).iso8601,
                                  time_max: (Time.new(2020, 12, 1)).iso8601,
                                 )
    events.items.each do |event|
      puts '-------------------------------'
      puts_event(event)
    end
  end

  #指定したevent情報を更新
  def update(event_id)
    #イベントを作成
    event = set_event(
      'updated test event',
      'updated test event',
      'updated',
      DateTime.new(2020, 8, 24, 12),
      DateTime.new(2020, 8, 24, 15)
    )

    #リクエストを送信し、レスポンスを受け取る
    response =  @service.update_event(
      @calendar_id,  #calendarID, 必須
      event_id, #編集したいeventのID
      event #挿入したいイベント(Google::Apis::CalendarV3::Event)
    )

    puts_event(response)
  end

  def delete(event_id)
    @service.delete_event(
      @calendar_id,
      event_id
    )
  end
end

Calendar.new.read
```

最後の行でメソッドを呼び出しているので、呼び出すメソッドを変更すれば他のメソッドも試すことができます。
やっていることは以下のとおりです。

* APIをたたくためのクラスからインスタンス（`Google::Apis::CalendarV3::CalendarService`）を宣言
* JSONファイルを使って事前に認証しておく
* インスタンスを用いてメソッドを呼び出す（APIをたたく）

また、予定の情報を格納して扱うため、`Google::Apis::CalendarV3::Event`クラスのインスタンスを利用しています。
予定のタイトル、詳細、日時などを格納しています。
[Class: Google::Apis::CalendarV3::Event](https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/CalendarV3/Event.html)

`BadRequest`が出る場合は、記載したフォーマットが指定されたものとずれていないか確認してみましょう。
自分は日時をDateTimeで管理している点で少しつまづきました。

それではCRUD操作を行っているメソッドについて見ていきます。

### create

`CalendarService`に対して`insert_event`を実行することでリクエストを送ります。
引数にはセットしたいカレンダーのIDと予定の内容を記述した`Event`クラスのインスタンスを入れます。
レスポンスとして、作成した予定の`Event`のインスタンスが返ってきます。

### read

`CalendarService`に対して`list_events`を実行します。
カレンダーのIDと取得したい期間を指定しています。
レスポンスとして、複数の`Event`のインスタンスが格納された`Events`が返ってきます。

`get_event`メソッドを利用すれば、特定の`Event`のIDを指定して取得することもできます。

### update

`CalendarService`に対して`update_event`を実行します。
引数にはカレンダーのID、更新したい予定を特定するためのID、更新箇所を記述した`Event`クラスのインスタンスを入れます。
予定のIDはレスポンスとして返ってくる`Event`クラスに入っているので控えておきましょう。
レスポンスとして、更新した予定の`Event`のインスタンスが返ってきます。

### delete

`CalendarService`に対して`delete_event`を実行します。
引数にはカレンダーのID、削除したい予定を特定するためのIDを入れます。

# まとめ

- Google Calendar APIを遣うための準備をした。
- RubyのプログラムからCRUD操作を一通り行った。

何かあればご連絡ください

# 参考文献

[[Ruby]Google Calendar APIから日程を取得しよう](https://qiita.com/flmil/items/bf7ed4b357f3576cf4da)
[Google Calendar APIを使用してみる #1](https://qiita.com/GodPhwng/items/4310586e1ec3bda4d91f)
[Using OAuth 2.0 for Server to Server Applications](https://developers.google.com/identity/protocols/oauth2/service-account)
[Calendars](https://developers.google.com/calendar/v3/reference/calendars)
[Class: Google::Apis::CalendarV3::CalendarService](https://googleapis.dev/ruby/google-api-client/latest/Google/Apis/CalendarV3/CalendarService.html)