AWS Lambda 関数から 別の Lambda 関数を呼び出す方法

Lambda から Lambda を呼び出す方法についてのまとめです。

# 環境

* macOS
* AWS Cloud9
* Node.js 8

# Lambda から Lambda を呼び出す方法

他にもあるようですが、その一つとして [Invoke](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/API_Invoke.html) を使います。

[Invoke] は、[AWS SDK](https://aws.amazon.com/jp/tools/) の [Lambda API](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html) に含まれるメソッドです。

## Invoke の基本的な使い方

基本的な使い方は次のようになります。

1. ライブラリを読み込みます。　`const aws = require('aws-sdk');`
2. Lambdaオブジェクトのインスタンスを取得します。　`let lambda = new aws.Lambda({apiVersion: '2015-03-31'});`
3. パラメーターをJavaScriptオブジェクト形式で準備します。　`const params = { Object }`
4. 実行します。　`lambda.invoke(params, (err, data) => {})`

# 実装

簡単な実装コードで Lambda から Lambda を呼べることを確認します。

## 呼び出し側

[API Gateway] から [POST]リクエストされた時に実行される Lambda関数です。

### コード

```kintoneWebhookRecordPost.index.js
const aws = require('aws-sdk');

"use strict";
let sc;
let result;
let response;

exports.handler = async (event, context, callback) => {
  try {
    const kintonePost = JSON.parse(event.body);
    console.log(kintonePost);
    sc = 200;
    result = "kintone comment POST success!";
    response = {
      statusCode: sc,
      headers: { "Content-type": "application/json" },
      body: JSON.stringify( result )
    };
    let lambda = new aws.Lambda({apiVersion: '2015-03-31'});
    const params = {
      FunctionName: "<arn>", // Lambda 関数の ARN を指定
      InvocationType: "RequestResponse",
      Payload: JSON.stringify(event)
    };
    console.log(params);
    lambda.invoke(params, (err, data) => {
      let res = data;
      if (err) {
        console.log("invoke error.");
        console.log(err);
        context.done(err, err);        
      } else {
        console.log("invoke success.");
        console.log("response:", res);
        context.done(null, res);
      }
    });
  } catch(err) {
    sc = 400;
    result = err.message;
    response = {
      statusCode: sc,
      headers: { "Content-type": "application/json" },
      body: JSON.stringify( result )
    };
  }
  callback(null, response);
};
```

### params 説明

lambda.invoke メソッドに渡すパラメータの説明です。

* FunctionName: Lambda Function ARN を指定します。
* InvocationType: InvocationTypeを指定します。デフォルトは "RequestResponse"です。
* Payload: 呼び出すLambda関数に渡す値を指定します。JSONをそのまま渡せないので、文字列に変換して渡しています。

### 呼び出される側の Labmda の作成

下記の拙稿を参考にしてください。
[AWS Cloud9 で AWS Lambda を開発する環境を構築してみる](https://qiita.com/sy250f/items/093405f206b551e03502)

---

## 呼び出される側

Lambda を作成したときの blue print [hello world] のコードを使います。
特に何もしませんが、呼び出された時にログを出力して、コールバックに呼び出し元から渡された内容を戻します。
成功すれば、CloudWatchログで出力が確認できるはずです。

### コード

```lambdaInvokeTest.index.js
"use strict";
console.log('Loading function');
exports.handler = async (event, context, callback) => {
    console.log('Received event:', JSON.stringify(event, null, 2));
    console.log('event.body:', JSON.parse(event.body));
    // console.log('value1 =', event.key1);
    // console.log('value2 =', event.key2);
    // console.log('value3 =', event.key3);
    // return event.key1;  // Echo back the first key value
    // throw new Error('Something went wrong');
    callback(null, JSON.parse(event.body));
};
```

### 呼び出される側の Labmda の作成

Lambdaアプリの上で右クリックして [Create Here] を選択。
[Function name] を入力します。 
<img width="672" alt="スクリーンショット 2019-10-21 17.50.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/396a893e-00ec-7713-1ced-b45c1eb7f402.png">

[blue print] は node.js [hello-world] を選択します。
<img width="672" alt="スクリーンショット 2019-10-21 17.49.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/038e93f7-cc67-f66c-9337-07c6389f810e.png">

トリガーは [none]
<img width="668" alt="スクリーンショット 2019-10-21 17.50.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/2816d63c-263e-324a-9a97-a7e5fcb3ba6c.png">

実行ロールは 自動
<img width="662" alt="スクリーンショット 2019-10-21 17.50.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/1918e39c-1dbd-1b20-1929-193856c925c5.png">

blue print の [hello-world] です。
<img width="923" alt="スクリーンショット 2019-10-21 17.53.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/405bd229-1a51-db0e-45dd-f82e9f2a3caa.png">

---

# 実行結果

Cloud9（ローカル開発環境） から リモート環境へデプロイして実行します。
Cloud9からリモートの API Gateway 経由で Lambdaを実行する

## Cloud9 から リモートのAPI Gateway を実行

テスト用のJSONを渡します。

```Paylode.json
{
    "param1": "value1"
}
```

<img width="856" alt="スクリーンショット 2019-10-22 12.46.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/7ee958fc-faa4-3f79-a693-cb7a29f864ad.png">

### ログの確認

CloudWatchログを確認します。

#### 呼び出し側のログ

<img width="975" alt="スクリーンショット 2019-10-22 13.04.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/5720bf71-78f4-0094-d2a9-353cc292dec9.png">

呼び出された側からステータスコードと値が戻ってきています。

```
response: { StatusCode: 200,
Payload: '{"param1":"value1"}' }
```

#### 呼び出された側のログ

<img width="945" alt="スクリーンショット 2019-10-22 13.04.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/d39b3b1a-82c0-b74f-ae82-a8f8e3acf40d.png">

呼び出し側から渡されたパラメーターが取得できています。

```
event.body: { param1: 'value1' }
```

---

## アプリから API Gateway 経由で Lambda を実行する

kintoneの Webhook を使い、下記の流れで確認します。

1. kintoneアプリにレコード追加
2. kintoneアプリの アプリの設定→Webhook→Webhookログ を確認
3. cybozu.com共通管理 の システム管理→監査ログ　を確認
4. AWS の CloudWatchログを確認

Webhookの設定は下記になっています。
URLに AWS API Gateway の API エンドポイント を設定しています。
<img width="848" alt="スクリーンショット 2019-10-22 14.40.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/5106b587-ee5e-1969-b3b5-400b06b5d125.png">

---

アプリにレコードを追加します。
<img width="501" alt="スクリーンショット 2019-10-22 14.39.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/af7b3f0a-6e02-7373-ad27-4b335d9b7dde.png">

<img width="1190" alt="スクリーンショット 2019-10-22 14.42.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/a3f0bfeb-8bb9-a787-4e69-3fa90cd61871.png">

---

Webhook のログを確認します。
<img width="1172" alt="スクリーンショット 2019-10-22 14.40.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/59532088-d9aa-b7ad-20e2-c51e70819417.png">

成功しているようです。
<img width="1184" alt="スクリーンショット 2019-10-22 14.43.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/232bb39b-ce8a-26de-5086-e43ab323f23d.png">

---

監査ログを確認します。
<img width="1190" alt="スクリーンショット 2019-10-22 14.44.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/fecd6195-c7d4-b6b0-9f8c-a1c7e201018c.png">

<img width="1197" alt="スクリーンショット 2019-10-22 14.44.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/e5e173b7-9f1a-5278-5d79-afc39510b522.png">

---

呼び出し側の AWS CloudWatchログです。
kintoneのレコード情報が渡ってきています。
<img width="945" alt="スクリーンショット 2019-10-22 14.54.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/d1c33881-efc4-6b9d-728d-294d847cc0f0.png">

---

呼び出される側の AWS CloudWatchログです。

呼び出し直後のログがあり、
<img width="639" alt="スクリーンショット 2019-10-22 15.01.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/38204b61-b94c-c5cf-df0d-d836066fa55f.png">

呼び出し側から渡された情報がログ出力されています。
<img width="617" alt="スクリーンショット 2019-10-22 15.23.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/75b3414e-8782-02a6-8cdd-07f2f16b1390.png">

kintoneレコードをJSONに変換して表示されています。
<img width="903" alt="スクリーンショット 2019-10-22 15.02.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214523/b500ea03-7ffa-ff82-d544-abc5582ce8a6.png">

外部から AWS API Gateway 経由で Lambdaを呼び出して、さらに別の Lambda を呼び出すことができました。

---

# 関連リンク

## AWS関連

* [Node.js 8.10 runtime now available in AWS Lambda](https://aws.amazon.com/jp/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/)
* [AWS LambdaからLambdaを呼ぶ](https://www.magata.net/memo/index.php?AWS%20Lambda%A4%AB%A4%E9Lambda%A4%F2%B8%C6%A4%D6#zfd75dd8)
* [Connpass APIをLambdaから扱う〜Lambdaの非同期呼び出しによる分散処理〜](https://ky-yk-d.hatenablog.com/entry/2018/08/05/211400)
* [AWS Lambda invoke](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Lambda.html#invoke-property)
* [Node.js での AWS Lambda Context オブジェクト](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-prog-model-context.html)
* [Node.js の AWS Lambda 関数ハンドラー](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/nodejs-prog-model-handler.html)
* [チュートリアル: Lambda 関数の作成と使用](https://docs.aws.amazon.com/ja_jp/sdk-for-javascript/v2/developer-guide/using-lambda-functions.html)
* [AWS SDK を使用して Lambda 関数を呼び出す際の再試行とタイムアウトの問題をトラブルシューティングする方法について教えてください。](https://aws.amazon.com/jp/premiumsupport/knowledge-center/lambda-function-retry-timeout-sdk/)
* [AWS SDK for JavaScript のドキュメント](https://docs.aws.amazon.com/sdk-for-javascript/index.html)
* [AWS SDK for JavaScript](https://docs.aws.amazon.com/ja_jp/AWSJavaScriptSDK/latest/index.html)

## その他

* [JavaScriptのasync/awaitがPromiseよりもっと良い](https://qiita.com/Anders/items/dfcb48d8b27ceaffb443)
* [Web API: The Good Parts](https://www.oreilly.co.jp/books/9784873116860/)
* [Promiseの使い方、それに代わるasync/awaitの使い方](https://qiita.com/suin/items/97041d3e0691c12f4974)