はまったりひらめいたり…とか…

Angularや.NETやAzureやその他色々。

AngularからGoogle Calendar APIでイベントを登録する

はじめに

この記事のソースコードは下記のバージョンで構成されています。

  • Angular: v12.1.3

参照されるタイミングによっては動作しない可能性がありますのでご留意ください。

やりたいこと

Front(Angular)のみで、任意のユーザーにサインインさせ、サインインしたユーザーのカレンダーに、Google Calendar APIを使用してイベント登録を行いたい。

想像以上にやり方が複雑怪奇な感じになったのでメモ書き程度に残しておきます。

GCPGoogle Calendar APIを使用するための準備

Google Calendar APIを有効にする

GCPのプロジェクト画面からAPIを有効にしていきます。

f:id:TakasDev:20210731212435p:plain

認証の設定

f:id:TakasDev:20210731212726p:plain

「認証情報を作成」から「OAuthクライアントID」を選択します

設定対象 設定内容
アプリケーションの種類 ウェブアプリケーション
名前 任意
承認済みのJavaScript生成元 http://localhost:4200
承認済みのリダイレクトURL http://localhost:4200

OAuth同意画面(スコープ)の設定

f:id:TakasDev:20210731213219p:plain

「OAuth同意画面」の設定の入力項目はほぼ任意な内容です。

「スコープを追加または削除」から必要なスコープを追加します。

必要なスコープはドキュメントを参考にします。今回はカレンダーにイベントを追加したいという目的です。

必要な処理は下記の通りなので、それぞれの処理で必要なスコープを確認しましょう。

  1. カレンダーのリストの取得
  2. 1.で取得したいずれかのカレンダーにイベントを登録

上記のドキュメントから必要なスコープは下記の通りでした

Angular側の実装

index.htmlの変更

GCPAPIをどうこうするのに使用するライブラリなんかはnpmなどで配布されていないように見受けられました。

ひとまずここは公式のドキュメント通りにスクリプトファイルをindex.htmlで読み込みます。

<!doctype html>
<html lang="jp">
<head>
  <script defer src="https://apis.google.com/js/api.js"></script> <!--追加-->
  <meta charset="utf-8">
  <title>LearnGoogleOAuth</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
</body>
</html>

各種@typesをインストール

とはいえTypeScriptな型の世界で開発を行いたいので、型定義をインストールします。

npm install --save-dev @types/gapi @types/gapi.auth2 @types/gapi.calendar

今回使用するGAPIのベース、認証、カレンダーAPIの型定義です。

型定義を使用したいファイル内で定義を読み込んでおきましょう

/// <reference types="gapi"/>
/// <reference types="gapi.auth2"/>
/// <reference types="gapi.calendar"/>

GAPIのLoad処理

GAPIのAuthやCalendarといったインスタンスgapiloadinitで生成されるようです。

それぞれのインスタンスをLoadする処理を実装します。

export class AppSerivce {
  ...
  private authClient!: gapi.auth2.GoogleAuth;

  clientLoad(): void {
    // gapi.authインスタンスを使用できるようにロードする(scopeはOAuthの同意画面で設定したScopeを指定)
    gapi.load('client:auth2', () => {
      this.authClient = gapi.auth2.init({
        client_id: environment.gapiClientId,
        fetch_basic_profile: true,
        scope: 'openid https://www.googleapis.com/auth/calendar.readonly https://www.googleapis.com/auth/calendar.events ',
      });
      // gapi.client.calendarを使用できるようにロードする
      gapi.client.init({
        discoveryDocs: ['https://www.googleapis.com/discovery/v1/apis/calendar/v3/rest']
      })
    });
  }
  ...
}

サインイン

上記でgapi.authインスタンスの準備ができている場合は下記のコマンドでサインインが実行できます。

  async signIn(): Promise<void> {
    const res = await this.authClient.signIn();
  }

f:id:TakasDev:20210731221133p:plain

カレンダーリストの取得

上記でgapi.client.calendarインスタンスの準備ができている場合は下記のコマンドでカレンダーリストの取得ができます。

  getCalendarList() {
    // gapi.client.calendarインスタンスが生成されていると使用できる
    const req = gapi.client.calendar.calendarList.list()
    req.execute((res) => {
      // resにカレンダー情報が返却される
    })
  }

イベントの登録

上記でgapi.client.calendarインスタンスの準備ができている場合は下記のコマンドでイベントの登録が行えます。

イベント登録時に使用できるパラメータはドキュメントを参考にしてください。

  registerEvents(calendarId: string): void {
    const event: gapi.client.calendar.EventInput = {
      summary: 'Test',
      start: {
        dateTime: '2021-07-31T00:00:00.000Z'
      },
      end: {
        dateTime: '2021-07-31T00:00:00.000Z'
      }
    };
    const req = gapi.client.calendar.events.insert({
      calendarId,
      resource: event
    });
    req.execute((res) => {
      // 結果がresに返却される
      console.log(res)
    });
  }
}

結果、下図のように登録を行えるようになりました。

f:id:TakasDev:20210731223051p:plain

gapi.cllient.calendarインスタンスを生成しないやりかた

gapi.client.calendarを介さずに、clientからリクエスト直接叩くことでもイベント登録は可能です。

  registerEvents(calendarId: string): void {
    const event: gapi.client.calendar.EventInput = {
      summary: 'Test',
      start: {
        dateTime: '2021-07-31T00:00:00.000Z'
      },
      end: {
        dateTime: '2021-07-31T00:00:00.000Z'
      }
    };
    const req = gapi.client.request({
      path: `/calendar/v3/calendars/${calendarId}/events`,
      method: 'POST',
      body: event
    });
    req.execute((res) => {
      // 結果がresに返却される
      console.log(res)
    });
  }
}

APIに対応するインスタンスの生成ができなかった場合や、インスタンス内にインターフェースがなかった場合なんかはこっちの方法になりますね。

サクッと検証したい場合もこっちのほうが楽だったりするかもしれません。

さいごに

APIに対応するインスタンスのLoadなど変わった実装が必要なので最初は面食らいました。

見たのはカレンダーだけですが他の@typesとか見るに、Googleが提供するOAuthを伴うWebAPIの実行は同じような実装でできそう?という印象です。

今回検証で実装した全体は下記のリポジトリに格納しました。

github.com