AWS Cloud9 で AWS Lambda を開発する環境を構築してみる

kintone の [Webhook](https://jp.cybozu.help/k/ja/user/app_settings/set_webhook.html) を理解する為に、AWS Lambda に API Gateway 経由で Webhook通知を投げてみます。

その前段階として、Cloud9 から Lambdaを実行する環境を整えます。

# 本記事のキーワード一覧

本記事では下記のキーワードについて取り上げています。

* AWS Cloud9
* AWS API Gateway
* AWS Lambda
* AWS CodeCommit
* Node.js

# AWS Cloud9 に Lambda 環境を設定する

[AWS Cloud9 の AWS Lambda チュートリアル](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/tutorial-lambda.html)を参考に進めます。

Cloud9環境のセットアップが終わっていれば、[ステップ 2: Lambda 関数と API を作成する]から進めます。

AWS Settings から Lambdaを実行するリージョンを選択します。（今回は東京リージョン）
<img width="1245" alt="スクリーンショット 2019-10-17 17.41.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/ff53a2db-d758-6699-ed70-7e6e792e8523.png">

AWS Resources をクリックして展開された場所にある、Lambda+ マークを押します。 
<img width="570" alt="スクリーンショット 2019-10-17 17.45.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/d3bef516-bb4a-0624-e9f8-7ff78fff3654.png">

Create serverless application ダイアログが開くので、下記を入力します。

* Function name に関数の名前を入力します。(kintoneWebhookTest)
* Application name に、サーバーレスアプリケーションの名前を入力します。(kintoneWebhookTestApp)
<img width="667" alt="スクリーンショット 2019-10-17 17.53.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/04109051-2b9d-1d21-8fea-bc0196b282b0.png">

runtimeに Node.js を選択して、blueprintは empty-nodejs を選択します。
 
<img width="677" alt="スクリーンショット 2019-10-17 17.56.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/0c5512fd-ca9a-711d-abbf-77b7a85281ac.png">

Function trigger は API Gateway を選択。 Path は / （スラッシュ）を入れます。
その他もチュートリアルの通りに進みます。

Finish を選択して少し経つと作成された Lambda関数が表示されます。
左側が Cloud9 上のローカルLambda関数。右側がAWS リソースに表示されるLambda関数です。
<img width="1244" alt="スクリーンショット 2019-10-17 18.23.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/ff083a3a-eda2-3747-a377-1e09c15e9900.png">

## AWS Lambda サービスから確認

Cloud9 で設定した Lambda を Lambda サービス画面から確認してみます。
<img width="1179" alt="スクリーンショット 2019-10-17 18.30.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/05aa9912-3a57-ee42-0ec9-a8b092e126f2.png">

作成されていることが確認できます。

# テストコードの実行

## テストコードの作成

[ステップ 3: 関数にコードを追加する](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/tutorial-lambda-add-code.html) を参考に、テストコードを入れ替えます。

最初にあったコードはコメントアウトして、コンソールに'kintoneWebhookTest'、event 
の中身をログ出力するように追加しています。

```index.js
// exports.handler = (event, context, callback) => {
//     // TODO implement
//     callback();
// };

'use strict';
console.log('kintoneWebhookTest');
exports.handler = (event, context, callback) => {

  console.log(event);

  if (event.body) {
    event = JSON.parse(event.body);
  }

省略
```
関数の内容は特に気にしないで進めます。

## テストコードの実行 

Cloud9 にて Lambda を実行します。

[ステップ 4: 関数をローカルで実行またはデバッグする](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/tutorial-lambda-run-or-debug-local-function.html) を参考に進めます。

作成した Lambda 関数の上で右クリック → Run → Run Local を選択します。
<img width="1015" alt="スクリーンショット 2019-10-18 7.19.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/afb5dfd7-5e94-fc01-ffad-c79db0e9237a.png">

Test payload 画面が開きます。
<img width="1033" alt="スクリーンショット 2019-10-18 7.20.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/924e6e23-9420-1f09-91ec-1687e46dc1dd.png">

テストコードに渡す入力値を JSON 形式で入力します。

```json
{
  "option": "date",
  "period": "today"
}
```

<img width="1244" alt="スクリーンショット 2019-10-18 7.27.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/6681aefc-9186-9477-9a63-a593a29c18f3.png">

Run 押下して実行します。
<img width="766" alt="スクリーンショット 2019-10-18 7.28.45のコピー.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/3031ac2b-18cc-2517-1c93-2516a222c725.png">

結果が Response として表示されます。

# テスト実行結果の確認

## テスト1

実行日は 2019年10月18日です。

ペイロード（JSONで送るデータ）

```json
{
  "option": "date",
  "period": "yesterday"
}
```

レスポンスとログを確認

```json
Response
{
    "statusCode": 200,
    "headers": {
        "Content-type": "application/json"
    },
    "body": "{\"month\":10,\"day\":17,\"year\":2019}"
}

Function Logs
2019-10-18 00:05:33.389 kintoneWebhookTest
2019-10-18 00:05:33.390 { option: 'date', period: 'yesterday' }
```

# テストコードの説明

コードにコメントを入れました。


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

[exports](https://nodejs.org/dist/latest-v12.x/docs/api/modules.html#modules_module_exports) は Node.js の機能です。 イ[handler](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-prog-model-handler.html) はイベント処理用の Lambda 関数内のメソッドで、exports.handler は handler 関数を エクスポートして外部から使えるようになります。

```index.js
  /**
  * payload で渡された JSON の option, period で 分岐します。
  * 1段目→ date 2段目→ yesterday, today, tomorrow, その他はエラー
  * 1段目→ time 現在の時間を返す
  * その他はエラー
  * 
  */
  switch(event.option) {
    case "date":
      switch(event.period) {
        case "yesterday":
          result = setDateResult("yesterday");
          sc = 200;
          break;
        case "today":
          result = setDateResult();
          sc = 200;
          break;
        case "tomorrow":

省略

    default:
      result = {
        "error": "Must specify 'date' or 'time'."
      };
      sc = 400;
      break;
  }

  const response = {
    statusCode: sc,
    headers: { "Content-type": "application/json" },
    body: JSON.stringify( result )
  };

  callback(null, response);

```

呼び出し元からのJSONデータを使い、分岐して戻り値を変えています。
callback は 第１引数に エラー、第２引数に レスポンスをセットします。
テストコードでは、エラーの場合はレスポンスのbodyに "error": "Must specify 'date' or 'time'." の文字列をセットして、第１引数のエラーには null をセットしています。

# エラーの確認

payloadの値を変更してエラーの出力を確認します。

```json
{
  "option": "date",
  "period": "nextday"
}
```

結果。
レスポンスにエラーが表示されます。

```json
{
    "statusCode": 400,
    "headers": {
        "Content-type": "application/json"
    },
    "body": "{\"error\":\"Must specify 'yesterday', 'today', or 'tomorrow'.\"}"
}
```

# 本環境へのデプロイ

ここまでは、Cloud9 にて Lambda関数を作成していました。（ローカル環境）
実際の AWS Lambda 環境に修正したコードをデプロイします。

## デプロイ

手順は [ステップ 6: 関数を本稼働環境で実行する](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/tutorial-lambda-run-deployed-function.html) の内容になります。

ローカルの Lambda関数を右クリック、[Deploy] を選択して本環境へデプロイします。
<img width="410" alt="スクリーンショット 2019-10-18 12.49.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/064e1de4-5542-29ab-a6fe-fc057c3a852a.png">

AWS Lambda サービスを選択して、デプロイしたコードを確認すると反映されていることが分かります。

<img width="1120" alt="スクリーンショット 2019-10-18 12.59.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/ac4634ea-29e3-0fe5-68ec-c967922e4d0c.png">

## 本環境で実行結果の確認

ローカルの Lambda関数を右クリック、[Run][Run Remote]を実行します。
レスポンスから結果が同じことを確認します。
<img width="474" alt="スクリーンショット 2019-10-18 12.50.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/14ee2794-9d68-f821-90d2-57369a2c65b1.png">

# API Gateway で確認

最終的には API Gateway 経由で外部から Lambda を実行することになるので、Cloud9 から　API Gateway を実行してみます。

## ローカル実行

### エラーを確認

最初の設定でエラーになることを確認します。

<img width="711" alt="スクリーンショット 2019-10-18 13.28.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/1a7ff08e-229c-8298-f35f-b570d0a7fca9.png">

設定を変更します。
メソッド：POST
パラメータ： 
{
  "option": "date",
  "period": "yesterday"
}

<img width="715" alt="スクリーンショット 2019-10-18 13.28.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/5b5939cb-5801-0048-ef3a-c1fe39558e58.png">

OKを確認します。

## リモート実行

リモートで確認します。
<img width="718" alt="スクリーンショット 2019-10-18 13.31.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/7bfe3c53-266b-2a21-066e-bdf55266061f.png">

OKを確認します。

CloudWatchのログも確認できます。
<img width="415" alt="スクリーンショット 2019-10-18 14.06.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/1905eca6-927d-392c-01ad-4c78de99744b.png">

# AWS CodeCommit によるソースコード管理

開発中のソースコードを管理するために、[AWS CodeCommit](https://docs.aws.amazon.com/codecommit/index.html) を利用します。

[AWS Cloud9 の AWS CodeCommit サンプル](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/sample-codecommit.html) を参考に環境を設定します。

## IAM グループの作成

今回グループポリシーは [AWSCodeCommitFullAccess] にしておきます。
<img width="827" alt="スクリーンショット 2019-10-19 7.56.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/0d0011b7-9f2d-4ec8-8995-8b3c75323eb6.png">

## リポジトリの作成

Cloud9を東京リージョンで作成していますので、東京リージョンのまま管理コンソールからCodeCommit サービスを選びます。

リポジトリ名を入力して作成します。
作成ができたら接続はCloud9のターミナルから行うので、コンソールを閉じます。
<img width="839" alt="スクリーンショット 2019-10-19 8.20.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/6f24b03a-1af4-1fe7-6dbd-86b9586f2bb2.png">

## Cloud9 から CodeCommit に接続する

[ステップ 3: 環境 をリモートリポジトリに接続する](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/sample-codecommit.html#sample-codecommit-connect-repo) を参考に進めます。

Cloud9 のターミナルからコマンドを入力して設定を行います。

## リポジトリにコードを追加

作成したコードをリポジトリに追加します。
今回は現在Git管理前の Cloud9 で作成したコードを、前段の CodeCommit で作成したリモートリポジトリに追加します。

[ローカルリポジトリを CodeCommit リポジトリに接続する](https://docs.aws.amazon.com/ja_jp/codecommit/latest/userguide/how-to-connect.html#how-to-connect-local) を参考に進めます。

```bash
$ ls -la
total 24
drwxr-xr-x 3 ec2-user ec2-user 4096 Oct 19 00:02 .
drwxr-xr-x 5 ec2-user ec2-user 4096 Oct 17 09:21 ..
-rw-r--r-- 1 ec2-user ec2-user  379 Oct 18 03:50 .application.json
-rw-r--r-- 1 ec2-user ec2-user   18 Oct 17 09:21 .gitignore
drwxr-xr-x 2 ec2-user ec2-user 4096 Oct 17 22:20 kintoneWebhookTest
-rw-r--r-- 1 ec2-user ec2-user  862 Oct 17 09:21 template.yaml
:~/environment/kintoneWebhookTestApp $ git init
Initialized empty Git repository in /home/ec2-user/environment/kintoneWebhookTestApp/.git/
:~/environment/kintoneWebhookTestApp (master) $ ls -la
total 28
drwxr-xr-x 4 ec2-user ec2-user 4096 Oct 19 00:09 .
drwxr-xr-x 5 ec2-user ec2-user 4096 Oct 17 09:21 ..
-rw-r--r-- 1 ec2-user ec2-user  379 Oct 18 03:50 .application.json
drwxrwxr-x 7 ec2-user ec2-user 4096 Oct 19 00:09 .git
-rw-r--r-- 1 ec2-user ec2-user   18 Oct 17 09:21 .gitignore
drwxr-xr-x 2 ec2-user ec2-user 4096 Oct 17 22:20 kintoneWebhookTest
-rw-r--r-- 1 ec2-user ec2-user  862 Oct 17 09:21 template.yaml
```

Cloud9のターミナルを開き、前段で作成したディレクトリに移って `git init` します。
Cloud9 上から作成した Lambda 関数は初めからディレクトリに [.gitignore] を含みます。
git init 後に、[.git] ディレクトリができている事が確認できます。

```bash
:~/environment/kintoneWebhookTestApp (master) $ git remote add origin https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/kintoneCustomizeRepo
:~/environment/kintoneWebhookTestApp (master) $ 
:~/environment/kintoneWebhookTestApp (master) $ git remote -v
origin  https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/kintoneCustomizeRepo (fetch)
origin  https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/kintoneCustomizeRepo (push)
```

`git remote add origin` してリモートリポジトリ情報をローカルに設定します。
`git remote -v` で確認します。

`git add`, `git commit` したら `git push` してリモートに反映します。
 
<img width="915" alt="スクリーンショット 2019-10-19 9.57.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/7f95d545-67b0-ee06-fe05-af7534223e78.png">

CodeCommit で確認すると追加されたファイルが確認できます。

# 新しい Lambda 関数の追加

Cloud9 から同じ Lambdaアプリケーション（Lambda関数のディレクトリ）に 新しい Lambda関数を追加します。

## Lambda関数の追加

Lambdaアプリの上で右クリック → [Create Here...] を選択。
<img width="300" alt="スクリーンショット 2019-10-19 15.30.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/65a99bb0-b979-2633-64f0-78dfc9a680b1.png">

関数名を入力。
<img width="692" alt="スクリーンショット 2019-10-19 14.50.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/23331e26-50c8-d48d-6f93-258388439855.png">

runtime を選択して、blueprint(雛形)から [api-gateway-hello-world] を選択。
<img width="667" alt="スクリーンショット 2019-10-19 14.52.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/f2e03a0b-e177-5b65-b6d3-7a51fc8e3686.png">

トリガーは [API Gateway]、Path は [/]、Security は [NONE] を選びます。
<img width="667" alt="スクリーンショット 2019-10-19 14.54.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/8b77baa7-b984-8256-306c-b961182808df.png">

実行ロールは前段で作成したロールを選択。
<img width="666" alt="スクリーンショット 2019-10-19 14.55.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/1257d5a5-b6f2-2adc-ca91-b733d1d5efea.png">

確認画面で Finish を選択。
<img width="670" alt="スクリーンショット 2019-10-19 14.56.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/061ec080-cbc5-7a82-902e-d9fc5d33b283.png">

エラーが出ますが、後ほど修正します。

## API Gateway の実行Pathを修正

作成された Lambda関数の上で右クリック→ [Edit Config] を選択して、[template.yaml]が開くので、[Path: /] を [Path: /recordpost] に修正して保存します。
<img width="221" alt="スクリーンショット 2019-10-19 15.23.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/0c5cb3d3-930c-5dc4-d3a4-d85b2f2b36b8.png">

## ローカルで実行

#### Lambdaローカル実行
<img width="714" alt="スクリーンショット 2019-10-19 15.46.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/9c6d1a2e-34a6-9267-3d96-78871b1cd3dd.png">

#### API Gatewayローカル実行
<img width="709" alt="スクリーンショット 2019-10-19 15.46.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/f4ad6aac-8b7d-2476-2834-1a870c43520f.png">

## リモートにデプロイ

新しく作成した Lambda関数の上で右クリック→ [Deploy]を選択。
<img width="301" alt="スクリーンショット 2019-10-19 15.52.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/7f58b14c-5a73-a7da-6a12-ac822a76d598.png">

## リモートで実行

#### Lambdaリモート実行
<img width="712" alt="スクリーンショット 2019-10-19 16.00.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/18249b37-5f2e-326c-12c4-5d7a73e8038e.png">

#### API Gatewayリモート実行
<img width="714" alt="スクリーンショット 2019-10-19 16.00.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/5ca711fe-212c-0f17-9eb8-d42c1f693061.png">

## リポジトリに反映

ここまでの変更をリモートリポジトリに反映しておきます。

```bash
git status
git add .
git commit -m "add new lambda function"
git push -u origin master
```

次は新しく作成した Lambda関数を修正して、kintone Webhook の通知から呼び出してみます。

---

# 関連リンク

### AWS Cloud9 から Lambdaを使う関連リンク

* [AWS Cloud9 の AWS Lambda チュートリアル](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/tutorial-lambda.html)
* [AWS Cloud9 Integrated Development Environment (IDE) で AWS Lambda 関数を操作する](https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/lambda-functions.html)
* [AWS Lambda の制限](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html)

### CodeCommit

* [AWS Cloud9 と AWS CodeCommit を統合する]()

### Node.js

* [Node.js ガイド](https://nodejs.org/ja/docs/guides/)
* [Node.js の AWS Lambda 関数ハンドラー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-prog-model-handler.html)
* [AWS Lambda ランタイム](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html)
* [カスタム AWS Lambda ランタイム](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/runtimes-custom.html)

### 書籍

* 実践 AWS Lambda 西谷圭介著