Lambda Destinations を SAM で試してみた

こちらは、[Fusic Advent Calendar 2019 - Qiita](https://qiita.com/advent-calendar/2019/fusic) の17日目の記事です。

## Lambda Destinations
[Introducing AWS Lambda Destinations \| AWS Compute Blog](https://aws.amazon.com/jp/blogs/compute/introducing-aws-lambda-destinations/)

Lambdaの実行結果（成/否）によって次に実行するリソースを指定できるようになりました。
これでLambdaからSDKで各サービスを直接呼び出したりする必要がなくなり、StepFunctionにお世話になる機会も少なるなるのではないでしょうか。

## SAM でどう書くのか試してみた

[Introducing AWS Lambda Destinations \| AWS Compute Blog](https://aws.amazon.com/jp/blogs/compute/introducing-aws-lambda-destinations/) を参考にサンプルをSAMで動くようにしてみます。
**公式ブログのほうではSNSを呼び出してますが、今回は両方Lambdaにします。**

### SAMのテンプレート設置

samのバージョンは `0.37` です

```shell
# ひな形作成
#   Which template source would you like to use? : 1 - AWS Quick Start Templates
#   1 - AWS Quick Start Templates  : 1 - node0.12.x
$ sam init
```

### 各ファイルを編集

`hello-world/app.js`を公式ブログを参考に編集

```JavaScript
exports.lambdaHandler =  function(event, context, callback) {
    var event_received_at = new Date().toISOString();
    console.log('Event received at: ' + event_received_at);
    console.log('Received event:', JSON.stringify(event, null, 2));

    if (event.Success) {
        console.log("Success");
        context.callbackWaitsForEmptyEventLoop = false;
        callback(null);
    } else {
        console.log("Failure");
        context.callbackWaitsForEmptyEventLoop = false;
        callback(new Error("Failure from event, Success = false, I am failing!"), 'Destination Function Error Thrown');
    }
};

exports.successHandler = async (event, context) => {
    console.log('successHandler was executed');
};

exports.failHandler = async (event, context) => {
    console.log('failHandler was executed');
};
```

`template.yaml` を下記のように変更

```yml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: lambda-distinations
Globals:
  Function:
    Timeout: 3

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs12.x
      Policies:
        - LambdaInvokePolicy:
            FunctionName: !Ref SuccessFunction
        - LambdaInvokePolicy:
            FunctionName: !Ref FailFunction
  SuccessFunction:
      Type: AWS::Serverless::Function
      Properties:
          CodeUri: hello-world/
          Handler: app.successHandler
          Runtime: nodejs12.x
  FailFunction:
      Type: AWS::Serverless::Function
      Properties:
          CodeUri: hello-world/
          Handler: app.failHandler
          Runtime: nodejs12.x
  EventInvokeConfig:
    Type: AWS::Lambda::EventInvokeConfig
    Properties:
        FunctionName: !Ref HelloWorldFunction
        Qualifier: "$LATEST"
        MaximumEventAgeInSeconds: 600
        MaximumRetryAttempts: 0
        DestinationConfig:
            OnSuccess:
                Destination: !GetAtt SuccessFunction.Arn
            OnFailure:
                Destination: !GetAtt FailFunction.Arn

Outputs:
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn
```

### ビルド・デプロイ
```shell
$ sam build
# 初回の場合
$ sam deploy --guided
# ２回目以降
$ sam deploy
```

### 実行確認

lambdaを直接実行して確認

```shell
# 成功パターン
$ aws lambda invoke --function-name {sam deploy で出力されたLambdaファンクション名} --invocation-type Event --payload '{ "Success": true }' response.json
{
    "StatusCode": 202
}

# 失敗パターン
$ aws lambda invoke --function-name {sam deploy で出力されたLambdaファンクション名} --invocation-type Event --payload '{ "Success": false }' response.json
{
    "StatusCode": 202
}
```

どちらもステータスコード 202 が返ってきていますが、これは実行結果ではなく、Lambdaの「実行」することに成功したことの通知です。

あとは、それぞれ Lambda関数の管理画面の「モニタリング > クラウドウォッチログ」でそれぞれ該当時間に呼び出されていることを確認できます。

## 詰まった点・分かっていない点

### 詰まった点
- 大元のLambdaにDestination先のLambdaを実行できるRoleを加えなきゃいけない点  
    - SAMでどうやって書くのか最初分からず少し詰まりました。
  
### 分かってない点

- API Gateway からの呼び出した場合のDestination先の実行方法
    - まだあまり時間かけてないのですがサンプルコードにAPI Gatewayくっつけてやってみたけど思った挙動にはなりませんでした。
    - もしわかる方がいらしゃったらコメントいただけると嬉しいです！