[保存版・初心者向け]AWS LambdaからGoogleカレンダーの予定を取得するのを世界一丁寧に解説

少しだけ実戦的な内容。いろいろ調べながら助けてもらいながら初めて作ったので、詳しい説明はできないかもしれませんが、できるだけ再現性を意識して易しく書こうと思います。

# ちょっと宣伝

[ノベルワークス](https://www.novelworks.jp/)という会社でアルバイトをしながら学ばせてもらったものをまとめております。自社サービス[リクシェア for kintone](https://reqshare.com/)も運営しておりますのでよろしければぜひ～。

# 実現したい機能

AWS Lambdaから複数のGoogleカレンダーたちの予定を取得。さらに、もう一工夫して、前回アクセスからのカレンダーの変更分だけを取得できるようにする。

# やること

実装の手順を簡単にまとめます：

- GoogleCloudPlatformでサービスアカウントを作成
- そのアカウントを取得したいGoogleカレンダーたちに共有設定する
- トークンを保存しておくためのDynamoDBテーブルを作成
- Lambda関数の作成

# 用意するもの

- Googleアカウント
- Googleカレンダー
- AWSアカウント
- Node.jsが使えるPC

# 手順

## １．サービスアカウントを作る

まずはGoogleカレンダー側の準備を進めていきましょう！
その第一歩として、[GoogleCloudPlatform](https://cloud.google.com/?hl=ja)でログインしましょう。もしGoogleアカウントを持っていない方はこの機会に作ってみてください♪

ページを開いてもらうと、**「コンソール」**に行けると思います。

<img width="956" alt="2018-05-08_17h19_57.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/322a735c-a873-e096-b02f-46584cf4ca0d.png">

こんな感じになっているかなと思います。
（この画面はすでにプロジェクトを作成済みなので、もしかしたら違うかもしれませんね。よく覚えてないです）

それでは、左のメニューから**「APIとサービス」**を選択してください。APIを利用するにはプロジェクトを作成する必要があるので、作成しましょう。

プロジェクト作成が完了したら、左側に新しく

<img width="191" alt="2018-05-08_17h27_54.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/1f68dd7d-7334-fb05-8a6b-4ce15254285e.png">

こんな感じのメニューが出ていると思います。「ライブラリ」を選択しましょう。こちらで、GoogleCalendarAPIを有効にします。

検索欄でgoogle calendarなどと入力すると、

<img width="204" alt="2018-05-08_17h29_34.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/a7ca5747-f7b4-319d-4e2a-ca491229bb7e.png">

こんなのが出てくるかと思います。クリックすると詳細画面に入るので、**「有効にする」**ボタンを押してみましょう。

さあ、ここまで出来たら、今回の手順である「サービスアカウント」を作りましょう。

「ライブラリ」メニューの下にあった**「認証情報」**メニューをクリックしてください。

<img width="191" alt="2018-05-08_17h27_54.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/1f68dd7d-7334-fb05-8a6b-4ce15254285e.png">

そこで**「認証情報を作成」**を押すと、いくつか選択肢が出てくるので、**「サービスアカウントキー」**を選択しましょう。

サービスアカウント名は任意で結構です。
「役割」についてですが、僕は正直よくわかっていません(笑)よくわからないときはとりあえず強そうな権限を与えておきましょう。**「Project -> オーナー」**を選択します。

<img width="603" alt="2018-05-08_20h33_02.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/d59bb78c-585d-e029-2107-0e86a6a36129.png">


キーのタイプはここではJSONにしておきます。

以上の設定が完了したら**「作成」**ボタンをクリックしましょう。このとき、キーの情報が書かれたjsonファイルがダウンロードされるかと思いますので、大切に保管しておきましょう。後ほど、AWS Lambda 関数を作成する際に用います。

完了すると、先ほどと同じ画面に戻りますが、サービスアカウントが作成されていることが確認できるかと思います。ここで**「サービスアカウントの管理」**というのが右側のほうにあると思うので、クリックすると、サービスアカウントのより詳細な情報を見ることができます。

<img width="741" alt="2018-05-08_18h04_49.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/2460269c-038f-75dd-0f81-5686f0de1266.png">


そこの、**「サービスアカウントID」**はコピーしておいてください。次のセクションですぐ使います。

ただし！！

おそらくサービスアカウントIDは２行にわたって表示されていると思いますが、**１行ずつコピーすることをお勧めします**。改行が入ってうまくいかないことがあります。僕はここで思うように動きませんでした。


## ２．Googleカレンダーに共有設定する

次に、予定を取得したいGoogleカレンダーにサービスアカウントを共有設定することで、カレンダーの情報を取得できるようにします。

カレンダーの設定画面に移ると、下の画面のような項目があるかと思います：

<img width="957" alt="2018-05-08_17h57_31.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/b4d6da0a-aa81-3ed4-4d03-4dd6c6678815.png">

ここでユーザーを追加します。この時に使うメールアドレスが、先ほどコピーしていただいた**「サービスアカウントID」**になります。

これでGoogle側の準備は完了です！！これからAWS側の準備に入っていきます！


（補足）
サービスアカウントを**複数**のカレンダーに共有設定しておくと、それらすべてのカレンダーの情報を取得することができます。便利ですね。


## ３．トークンを保存しておくためのDynamoDBテーブルを作成

続きまして、トークンを保持しておくためのデータベースをDynamoDBで作成しようかと思います。

ここで**トークン**という曖昧な言葉を出してしまいましたので、ここでのトークンの意味について補足をしておきます。
GoogleCalendarAPIで予定を取得する際、APIが**nextSyncToken**という名前のある文字列を返してくれます。これを次に予定を取得する際にパラメータとしてAPIに送信すると、なんと、APIが前回のアクセスからの**変更分のみ**を返してくれるようになります！！

ですので、「いいからとにかく予定が取得できればいいんだ！」という方はここのセクションは見なくても全然大丈夫です(笑)ただ、次のセクションで紹介するLambda関数のコードの中にDynamoDBを操作するコードが含まれますので、その点だけご了承ください。


それでは、AWSのコンソール画面に移りましょう。AWSのアカウントを持っていない方は、アカウントを作ってください。

<img width="918" alt="2018-05-08_18h19_25.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/0f065261-9fdf-1207-1836-cccb503f5b24.png">

たくさんサービスがありますが、その中から**DynamoDB**を選択してください。ダッシュボードに移ると思いますので、そこで**「テーブル作成」**を押してテーブルを作ります。

<img width="923" alt="2018-05-09_09h59_50.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/0fe06340-8422-7d7b-07c8-88f1d30ccb08.png">


ここでは、こんな感じのテーブル設計にしようかと思います：

```javascript
{
    "mainKey": "testItem",
    "token": {
        "カレンダーID1": "対応するトークン",
        "カレンダーID2": "対応するトークン",
        //以下、同じ構造が続く
    }
}
```

正直、この辺は好きに決めてもらってかまいませんが、説明がしやすいので考えうる最もシンプルな構造にしました。お粗末ですね。

ですので、上のような構造にしたければ、**パーティションキー**として"mainKey"を設定してテーブルを作成しましょう。１分もたたずにテーブルが出来上がります。AWSのすごいところですね。簡単で速い。

最後に、あらかじめ項目（アイテム）を追加しておきましょう。
テーブルを作成したときに出てくるメニューから**「項目」**を選択し、追加する項目を手動で作成することができます。ここではパーティションキーに何かてきとうな値を書いて保存しておきましょう。この値はまた後で使います。

<img width="911" alt="2018-05-09_10h01_38.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/03ea8c4d-b409-746c-a021-f0ea05f03d1f.png">


以上で準備は完了です。いよいよ次のセクションでLambda関数を実装していきます！！


## ４．Lambda関数を作成

次にLambda関数をいよいよ実装していきます！
とりあえず関数を作成しておいて、そのあとにコーディングをしようかなと思います。

それでは、AWSのコンソールから、**Lambda**を選び、関数を作成しましょう。ここでも最低限シンプルなものを作ろうかなと思います。

<img width="872" alt="2018-05-08_18h36_51.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/60439b38-7ed4-06e3-0125-8589235e3680.png">

**「一から作成」**を選択し、ランタイムはここでは**Node.js.6.10**を選択することにします。

最後に**ロール**なのですが、ここでは注意が必要です。

そもそもAWSにおける**ロール**とは、AWSの各リソースに付与することのできる権限を記したもので、ここでは、今から作成するLambda関数がAWSのほかのどんなリソースにアクセスしたりできるかを規定するロールを選択することになります。

今回の記事では、DyamoDBにアクセスする権限が必要なので、もしそのようなロールがない場合は自分で作成しなければなりません。

ロールの作成については、最後に「補足～ロールの作成について～」というセクションで説明をしてみましたので、よくわからない方は参考にしてみてください。

そして、以上の選択が完了したら関数を作成しましょう！こちらもクリックしたらすぐできます。AWSすごいですね。

<img width="896" alt="2018-05-08_18h59_17.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/bf56aefa-3795-c3e6-0697-f9a51d8b0cc0.png">


関数は作成できたので、いよいよコードを書いていこうと思います。
なお、簡単なコードならインラインで（AWS Lambdaの画面上で）書くことができますが、今回はNode.jsのモジュールの一つ**googleapis**を使いたいので、ローカルのNode.jsが使える環境でコーディングをしたいと思います。


## ５．Lambda関数のコーディング～ファイル等の準備～

それでは、いよいよコードを書きたいと思います。なお、今回のコード作成で主に参考にさせてもらったwebページは


- https://qiita.com/Fujimon_fn/items/9a0ec4eca75ce0784722
- http://isd-soft.com/tech_blog/accessing-google-apis-using-service-account-node-js/
- https://developers.google.com/calendar/
- https://qiita.com/Yuki_BB3/items/83198b4d9daca7ccd746


です。２つ目と３つ目は英語ですが、ぜひぜひご覧になってください！

作業フォルダをGoogleCalendarとします。フォルダの中のファイル構成は以下のようにします：

```text
GoogleCalendar
     |--- index.js
     |--- privatekey.json
```

ここで、privatekey.jsonは、はるか昔にサービスアカウントを作成した際にダウンロードされたJSONファイルです。名前を変えておきましょう。

以下ではコマンドラインでの操作を想定しております。上で作成したディレクトリに移動してください。

そして、GoogleAPIを使うためのNode.jsのモジュールをインストールすることをしておきましょう。一応、npmのバージョンも確認しておいてください。

```text
$ npm --version
> 5.6.0
```

僕の場合はこうなりました。

そして、インストールの手順としては

```text
$ npm init
$ npm install googleapis --save
```

となります。
（npm initとしたとき、いろいろ聞かれますが、全部エンターキーを押してガン無視しましょう。）

上の２つのコマンドを実行すると、モジュールがたくさん入ったフォルダ**「node_modules」**が生成されます。したがって、現在の作業フォルダの中身はこんな感じになっているかと思います：

```text
GoogleCalendar
     |--- node_modules
     |--- index.js
     |--- privatekey.json
```

ほかにもjsonファイルが生成されたりしますが、以降で使うのは上の３つだけです。


## ６．Lambda関数のコーディング～index.jsを書く～

さて、いよいよindex.jsファイルにコードを書いていくわけですが、まずは今回僕が作成したコードをとにかく見てもらい、それについて解説をしていくという形にしたいなと思います。

以下がindex.jsファイルの中身です：

```javascript:index.js
'use strict';

//PART1
/////////////////////////////////////////////////////////////////

//GoogleCalendarAPIの処理で必要なもの
const {google} = require('googleapis');
const calendar = google.calendar('v3');

//privatekey.jsonを読み込む
const privatekey = require('./privatekey.json');

//認証に必要な設定
const jwtClient = new google.auth.JWT(
    privatekey['client_email'],
    null,
    privatekey['private_key'],
    ['https://www.googleapis.com/auth/calendar']
);

//DynamoDBにアクセスするために必要なもの
const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient({
    region: 'ap-southeast-1' //ここはリージョンを適切に変えてください。僕はシンガポールリージョンで作っているのでこうです。
});

//テーブル名とパーティションキー
const tableName = 'testTableOta';
const partitionKey = 'testItem';

///////////////////////////////////////////////////////////////////////

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

    //PART2
    //////////////////////////////////////////////////////////////////

    //認証処理
    .then(function(){
      
        return new Promise(function(resolve, reject){
            
            //authenticate request
            jwtClient.authorize(function (err, tokens) {
                if (err) {
                    reject(err);
                }else {
                    console.log("認証成功");
                    resolve();
                }
            });
              
        });
        
    })
    /////////////////////////////////////////////////////////////////////

    //PART3
    ////////////////////////////////////////////////////////////////////

    //Dynamoからまずは必要なデータを取得してくる
    .then(function(){
        
        return new Promise(function(resolve, reject){
            
            const params = {
                'TableName': tableName,
                'Key': {
                    'mainKey': partitionKey
                }
            };
            dynamo.get(params, function(error, tableData){
                if(error){
                    console.log("DynamoDBからの取得失敗 error:", error);
                    reject(error);
                }else{
                    console.log("DynamoDBからの取得成功！");
                    console.log("DynamoDB datas:", tableData['Item']['token']);
                    resolve(tableData['Item']['token']);
                }
            });
            
        });
        
    })
    ///////////////////////////////////////////////////////////////////////////////////


    //PART4
    ///////////////////////////////////////////////////////////////////////////////////
    
    //カレンダーリスト取得
    .then(function(token){
        
        return new Promise(function(resolve, reject){

            calendar.calendarList.list({
                'auth': jwtClient
            }, function(error, resp){
                if(error){
                    reject(error);
                }else{
                    const tokenAndCalendarList = {
                        'token': token,
                        'calendarList': resp.data.items
                    };
                    resolve(tokenAndCalendarList);
                }
            });
            
        });
        
    })
    //////////////////////////////////////////////////////////////////////////////////


    //PART5
    //////////////////////////////////////////////////////////////////////////////////
    
    //カレンダーたちから予定を取得する
    .then(function(tokenAndCalendarList){
        
        //カレンダーリスト、トークン
        const calendarList = tokenAndCalendarList['calendarList'];
        const token = tokenAndCalendarList['token'];
        
        //取得するカレンダーの個数（この数だけあとでPromiseを生成する）
        const len = calendarList.length;
        
        //tokenを記録しておくための連想配列
        const syncTokens = {};


        //PART5.5
        //////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////

        //動的に生成するPromiseオブジェクトたちを記録しておく連想配列
        const promises = {};

        //動的にPromiseオブジェクトを生成する
        for(let i = 1; i <= len; i++){
            
            //i番目のカレンダーID
            const calendarId = calendarList[i-1]['id'];
            
            //Promise生成
            promises['promise' + i] = new Promise(function(resolve, reject){
                
                const params = {
                    'auth': jwtClient,
                    'calendarId': calendarId
                };
                //tokenが存在すれば、パラメータに追加して、カレンダーの予定の変更分のみ取得する
                if(token){
                    params['syncToken'] = token[calendarId];
                }
                
                //予定の取得
                calendar.events.list(params, function(error, {data}){
                    
                    //カレンダーの予定達
                    const events = data.items;
                    
                    //nextSyncTokenを記録
                    syncTokens[calendarId] = data.nextSyncToken;
                    
                    //それぞれのPromiseでは、カレンダーの予定を返す
                    resolve(events);
                    
                });
                
            });
            
        }

        ///////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////


        //以上でPromiseたちを使ってそれぞれのカレンダーたちの予定を取得し、トークン情報を記録できた
        const arrayPromises = [];
        for(let j = 1; j <= len; j++){
            arrayPromises.push(promises['promise' + j]);
        }
        return Promise.all(arrayPromises).then(function(eventsList){
            
            //DynamoDBへトークンの情報を保存する処理をする
            const item = {
                'mainKey': partitionKey,
                'token': {}
            };
            for(let k = 0; k < len; k++){
                const calendarId = calendarList[k]['id'];
                item['token'][calendarId] = syncTokens[calendarId];
            }
            const params = {
                'TableName': tableName,
                'Item': item
            };
            
            return new Promise(function(resolve, reject){
                
                //DyamoDBテーブルへの更新処理
                dynamo.put(params, function(error, data){
                    if(error){
                        console.log(error);
                        reject(error);
                    }else{
                        console.log("DynamoへのPUT成功 data:", data);
                        resolve(eventsList);
                    }
                });
                
            });
            
        });
        
    })
    ////////////////////////////////////////////////////////////////////////////////////////



    //PART6
    /////////////////////////////////////////////////////////////////////////////////////

    .then(function(eventsList){
        //成功！
        callback(null, eventsList);
    })
    .catch(function(error){
        //失敗。。。
        callback(error);
    });
    
};
```

とても長くなりましたが、これですべてです。あとはテーブルネームやパーティションキーの名前を適切に変更すれば動くようになっています。

以下でコードの解説をしていきます。

## ７．Lambda関数のコーディング～コード解説～

解説といっても、まだ僕自身理解できていないところがいくつかあるので、そこはご容赦ください。わかったら随時更新していきます。

### PART1

ここでは、今回のコード全体に必要な情報で最初に準備できるものを先に定義しています。

まずはGoogleCalendarAPIにアクセスするためのオブジェクトを用意：

```javascript
const {google} = require('googleapis');
const calendar = google.calendar('v3');
```

その次のコードでは、サービスアカウントを作成したときに取得したJSONファイルを読み込んでいます。JSONの形がそのままオブジェクトになって使うことができるので、たとえば`privatekey['client_email']`などど要素を参照することができます。

```javascript
const privatekey = require('./privatekey.json');
```


その次は、認証に必要な設定です：

```javascript
const jwtClient = new google.auth.JWT(
    privatekey['client_email'],
    null,
    privatekey['private_key'],
    ['https://www.googleapis.com/auth/calendar']
);
```

認証とは、要はいつもやっている「メールアドレスとパスワードを入力するやつ」です。それの別バージョンだと思ってください。

また、４つ目の引数は、GoogleCalendarAPIの**スコープ**です。詳しくは[こちら](https://developers.google.com/calendar/auth)をご覧ください。僕もよくわかってないです。

PART1の最後では、DynamoDBの操作の準備をしています。

```javascript
//DynamoDBにアクセスするために必要なもの
const AWS = require('aws-sdk');
const dynamo = new AWS.DynamoDB.DocumentClient({
    region: 'ap-southeast-1' //ここはリージョンを適切に変えてください。僕はシンガポールリージョンで作っているのでこうです。
});

//テーブル名とパーティションキー
const tableName = 'testTableOta';
const partitionKey = 'testItem';
```

`dynamo`がDynamoDBのオブジェクトを表し、これにputやgetというメソッドがあるので、それによってテーブルのデータを取得したり更新したりできます。

`tableName`は、ご自身が作成したテーブルの名前にしてください。
`partitionKey`は、テーブルを作成したときに手動で作成した項目のパーティションキーの値を書いてください。


### PART2

ここでは実際に`jwtClient`を用いて認証処理をしています。

### PART3

ここでは、DynamoDBからトークンのデータを取得してきます。

注意しておきたいのは、リクエストパラメータです：

```javascript
const params = {
    'TableName': tableName,
    'Key': {
        'mainKey': partitionKey
    }
};
```

TableNameの方はテーブルの名前で大丈夫です。
問題はKeyの値です。ここには、テーブルの要素を確定させるための情報をオブジェクトの形で渡してやる必要があります。

今回の場合は、要素を一意に決めるのはパーティションキーだけですので、それだけ記述すればOKです。

ただ、テーブルを作成するときに**ソートキー**を追加して、**「パーティションキー＋ソートキーの組み合わせで一意に決める」**というテーブル設計にしている場合は、ソートキーの情報も記述してください。


また、取得したデータ（tableData）はオブジェクトの形になっており、今回のテーブル設計では以下のような構造になっています

```text
tableData: { 
    Item: {
        mainKey: 'testItem',
        token: {
            'カレンダーID1': '対応するトークン' 
        } 
    }
}
```

ですので、次のthenでの処理には`tableData['Item]['token']`の部分だけを渡すようにしています。

なお、tokenの部分は、サービスアカウントを共有したカレンダーの数だけ増えていきます。

### PART4

ここでは、**カレンダーリスト**を取得しています。カレンダーリストとは、サービスアカウントを共有設定したカレンダーたちそれぞれの情報です。たとえば、カレンダーIDなどの情報が記されています。

具体的には、`resp.data.item`がそれに相当します。

なお、次のthenの処理ではトークンとカレンダーリストの情報を使いますので、それを連想配列にまとめてから後続の処理に引き渡しています。



### PART5(前半)

ここでは、PART4でから引き継いだトークンとカレンダーリストの情報を用いて、実際に各カレンダーの予定を取得していきます。ここが本当にやりたかったところです。

ここで、僕がコーディングするうえで意識した点を述べておきます：


- 各カレンダーから予定を取得するという**非同期処理**は、それぞれ**個別**にPromiseを用意して管理したい。
- ただ、カレンダーの個数は事前に知ることはできないので、任意個のカレンダーに対応できるように動的にPromiseを生成して処理しないといけない。
- 予定を取得するときに、一緒に**nextSyncToken**も取得できる。
- 予定の取得がすべて完了したら（＝トークンをすべて取得したら）、DynamoDBの項目の内容を更新しないといけない。


このくらいです。PART5の前半では、その準備をしています。
特に、変数`len`がポイントです。これの個数に合わせて動的にPromiseを生成していきます。


### PART5.5

ここでは、実際にPromiseを使って非同期処理を実行しています。

```javascript
//動的に生成するPromiseオブジェクトたちを記録しておく連想配列
const promises = {};

//動的にPromiseオブジェクトを生成する
for(let i = 1; i <= len; i++){
            
//i番目のカレンダーID
const calendarId = calendarList[i-1]['id'];
            
    //Promise生成
    promises['promise' + i] = new Promise(function(resolve, reject){
                
        const params = {
            'auth': jwtClient,
            'calendarId': calendarId
        };
        //tokenが存在すれば、パラメータに追加して、カレンダーの予定の変更分のみ取得する
        if(token){
            params['syncToken'] = token[calendarId];
        }
                
        //予定の取得
        calendar.events.list(params, function(error, {data}){
                    
            //カレンダーの予定達
            const events = data.items;
  
            //nextSyncTokenを記録
            syncTokens[calendarId] = data.nextSyncToken;
                    
            //それぞれのPromiseでは、カレンダーの予定を返す
            resolve(events);
                    
        });
                
    });
            
}
```

まずポイントは、**いったん空の連想配列を準備しておき、それに要素を追加していくことで、「キー＝変数名」、「値＝変数の値」とみなすことで仮想的に変数を生成している**ことです。

これについては、[こちらの記事](https://qiita.com/Rikiya_Ota/items/6a814bf736ebcafe0741)で簡単にまとめましたので是非ご覧ください。

パラメータの作成では、認証情報、カレンダーIDが必要なのですが、それに加えてトークンの情報がある場合には、それも追加でパラメータに含ませています。

そして、`calendar.events.list`の部分で実際に予定を取得しています。変数`data`の構造は以下のようになっています：

```text
data = {
    (なんかその他いろいろ)
    nextSyncToken: 'xxxxxxxxxxxxxxxxxxxxxx',
    items: [ 
              {予定１},
              {予定２},
              {予定３},
              ・・・
    ]
}
```


見てわかるように、この中に**nextSyncToken**の情報がありますので、これを連想配列で記録しておきます。


### PART5(後半)

生成したPromiseたちを並列で管理するために`Promise.all`を用います。

先ほど述べたように、Promiseたちの処理がすべて完了したら、DynamoDBにトークンの情報を更新します。
そのときのパラメータは以下のような構造にしなければいけません：

```text
{
    'TableName': tableName,
    'Item': {
        (この中はテーブルに保存する項目の情報)
    }
}
```

`Item`の中は、テーブルに保存する項目をオブジェクトの形でまとめたものを与えてやる必要があります。ただし、後で`put`メソッドで更新をするのですが、該当する要素が存在しない場合はテーブルに追加保存してくれ、該当する要素が存在している場合は、与えられた情報に基づいて更新してくれます。


### PART6

最後は開発者側の処理です。

最後のthenは、ここまで無事に処理が到達すれば、それは処理が成功（少なくともエラーは起きていない）したことを意味するので、Lambdaコンソールに表示されるようにします：

```javascript
callback(error, success);
```

成功なので、第1引数には`null`を渡して、第２引数にはなにか適当なものを渡しておきます。僕のコードでは取得したすべての予定を渡していますが、ここは何でもいいです。

また、`catch`は、どこかのPromise処理でエラーが起こったり`reject`関数が呼ばれたときに実行される処理を記述しているところです。要は、エラーをここですべて捕捉しているんですね。ですので、エラーオブジェクトをcallback関数の第１引数に渡しています。

## ８．zipファイルをLambdaにアップロードしよう

もう一度フォルダの構成を確認しましょう：

```text
GoogleCalendar
     |--- node_modules
     |--- index.js
     |--- privatekey.json
```

Lambdaにこれらをアップロードする際には、これらをzipファイルにまとめてからアップロードしなくてはいけません。

この時注意したいのは、作業ディレクトリの中にある「node_modules、index.js、privatekey.json」をきちんと指定して、これらをzipファイルにまとめることです。

それができましたら、Lambdaコンソーの少しスクロールしたところに以下のような「関数コード」という部分があるかと思います：

<img width="888" alt="2018-05-09_10h33_59.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/9e679d5a-2e50-2378-edff-bfeeffe06624.png">

ここで、画像にあるように**「1.zipファイルをアップロード -> 2.アップロードをクリックしてファイルを選択 -> 3.保存」**としてください。

これで作成したコードをLambdaに適用することができました！


## ９．テストをしてみよう

ここまでできたら取得の準備は完了です！お疲れさまでした！！

それでは、実際に作成したLambda関数を実行してみましょう。Lambdaコンソールの「テスト」というところをクリックしてみましょう。

<img width="896" alt="2018-05-08_18h59_17.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/48762a4f-d4e4-e2e2-f22d-a2d180bc9e1c.png">

テストイベントを作成するよう指示が出ますが、今回はそのイベントオブジェクトは使わないので、てきとうで結構です。
（本来は、Lambda関数の呼び出し元から送られてきたデータを想定して記述しないといけないですが、今回の記事では呼び出し元のところまでは触れません）

そして、もう一度「テスト」の部分をクリックしましょう！
うまく成功しましたでしょうか？？何かしらエラーが出ましたら、Lambdaコンソール上でも確認できますし、CloudWatchというAWSのサービスでも確認ができます。


# まとめ

以上が僕が実際にやってみた一連の作業です。僕自身まだ完全に理解できていませんが、随所にポイントがたくさん散りばめられていて、勉強になる制作物になったと思います。

長文でしたが、最後まで読んでくださいましてありがとうございました。


## 補足～ロールの作成について～

AWSコンソールの**「IAM」**を選択し（←これ、アイアムって読むそうですよ）、「ロール」を選択しましょう。そこで新しいロールの選択ができます。

<img width="873" alt="2018-05-08_18h42_48.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/69182dd0-37d4-7eb2-27eb-056774b2bd94.png">

今回はLambdaに権限を与えるロールを作成したいので、上野画像のように選択して、「次のステップ：アクセス権限」をクリックしましょう。

すると、**「ポリシー」**と呼ばれる権限を選択することができます。ここで、先ほど説明したように、DynamoDBにアクセスするための権限を付与することになります。

下画像のように、検索欄にDynamoDBとでも入力すると、**「AmazonDynamoDBFullAccess」**というロールが選択できることがわかります。左のチェック欄にチェックをつけましょう。

<img width="778" alt="2018-05-08_18h46_34.png" src="https://qiita-image-store.s3.amazonaws.com/0/243500/81e5bf19-bc86-ad27-c5c1-cd702872d983.png">

ついでに、ログを確認したりするためにCloudWatchへの権限も付与しておくといいでしょう。**「CloudWatchFullAccess」**というロールにもチェックを付けてください。

以上でロールを作ることができました。AWSのサービスでうまく動作しないときに、適切に権限を付与できていないという場合もよくあるみたいなので、気を付けてください。