Node.js
AWS
GoogleCalendar
lambda
gcp

Google Calendar API V3をAWS Lambda(node.js)から叩いてカレンダーを取得する

やりたいこと

Google Calendarの「特定のユーザーとの共有」の機能を使って、Botにカレンダーへの編集権限を与えることでBotがそのカレンダーの中身を読んだり、内容を追加したりできるようにしたいと思います。(この記事ではとりあえずカレンダーを取得するところまで。)
例えば予約システムを作ったとして、クライアントが予約状況をGoogleカレンダーからも確認したいという時に、予めクライアントのGoogleカレンダーに予約システムのBotを共有ユーザーとして追加しておくことで、予約状況をBotが随時クライアントのGoogleカレンダーに書き込んでいくといったケースが考えられます。

サービスアカウントの作成

サービスアカウントを作成すると、そのサービスアカウントをGoogleカレンダーの共有ユーザーとして追加できるようになります。(参考: GCP Service Accountを理解する

  1. https://console.developers.google.com でプロジェクトを選択or新規作成する
  2. 「ライブラリ」>「Google Calendar API」を選択して、Google Calendar APIを有効化する
  3. 「認証情報」>「認証情報を作成」で、「サービスアカウントキー」を選択
    スクリーンショット 2018-02-09 14.31.32.png

  4. サービスアカウントを作成して秘密鍵をJSON形式で保存する。(この時のサービスアカウントIDが、Google Calendarで共有ユーザーとして招待する時のメールアドレスになります。サービスアカウント「役割」はよくわからなかったので適当に「サービスアカウントユーザー」にしています。)
    スクリーンショット 2018-02-09 14.41.38.png

カレンダーの共有ユーザーにサービスアカウントを追加

カレンダーの共有ユーザーにサービスアカウントを追加するには、Googleカレンダーの共有設定からサービスアカウントID(メールアドレスの形式のもの)を共有ユーザーとして追加するだけです。サービスアカウントIDの確認方法は以下のスクショのとおりです。
スクリーンショット 2018-02-09 15.02.13.png
スクリーンショット 2018-02-09 15.02.25.png

Lambdaにアップロードする関数パッケージを作成

ライブラリを入れる

  1. プロジェクト用のディレクトリ(sample-project)の中にindex.jsを作成
  2. そのディレクトリで下記のコマンドを実行してAPIのNode.js用のライブラリを入れる
npm install googleapis --save
  1. 先程保存したサービスアカウントの秘密鍵のJSONをディレクトリに入れる

ディレクトリ構造はこんな感じです。

  • sample-project
    • node_modules(ライブラリ)
    • index.js(ここにコードを書いてく)
    • privatekey.json(秘密鍵のJSON)

index.jsの中身

このサンプルでは、サービスアカウントが共有ユーザーとして追加されているカレンダーの一覧を取得します。

index.js
'use strict';
const {google} = require('googleapis');
const privatekey = require('./privatekey.json');

exports.handler = (event, context, callback) => {

  Promise.resolve()
  .then(function(){
    return new Promise(function(resolve, reject){
      //JWT auth clientの設定
      const jwtClient = new google.auth.JWT(
             privatekey.client_email,
             null,
             privatekey.private_key,
             ['https://www.googleapis.com/auth/calendar']);
      //authenticate request
      jwtClient.authorize(function (err, tokens) {
        if (err) {
          reject(err);
        } else {
          console.log("認証成功");
          resolve(jwtClient);
        }
      });
    })
  })
  .then(function(jwtClient){
    return new Promise(function(resolve,reject){
      const calendar = google.calendar('v3');
      calendar.calendarList.list({
         auth: jwtClient
      }, function (err, response) {
         if (err) {
             reject(err);
         }else{
          console.log('CalendarList:', response.data.items);
           resolve(response.data.items);
         }
      });
    });
  })
  .then(function(result){
    callback(null, result);
  })
  .catch(function(err){
    callback(err);
  });

};

Lambdaへのアップロードとテスト

ZIPファイルの作成

ZIPファイルを作成するときですが、プロジェクトフォルダを圧縮するのではなく、中身を全選択して圧縮してZIPファイルを作成して下さい。
スクリーンショット 2018-02-09 15.11.30.png
こうしてできたZIPファイルを、予め適当に作成しておいたLambda関数にアップロードします。

  1. コードエントリタイプを「.ZIPファイルをアップロード」に変更
  2. 作成したZIPファイルを選択して右上の「保存」を押す
  3. 「テスト」ボタンを押して実行 スクリーンショット 2018-02-09 15.17.58.png

実行すると、予めサービスアカウントを共有ユーザーとして追加しておいたカレンダーの名前や情報がコンソールに出力されているはずです。

感想

意外と簡単でした。ただ気になったのは、実行時間の長さです。上記サンプルコードでだいたい1300ms~1500msかかっています。Lambdaの特性上、認証情報を保持しておくことができずに毎回認証リクエストを送る必要があるので、あまり実用的ではないかなと思いました。それでもさくっと試したいときはやっぱりLambdaは便利です。

参考

限定共有のGoogleCalendarをLambda(Python)で操作する【cloudpack大阪】
Node.js Quickstart - Google Calendar API
Accessing Google APIs using Service account in Node.JS
google-api-nodejs-client GitHub