kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Amazon API Gateway から Amazon EventBridge への直接統合を AWS SAM と OpenAPI で構築する

Amazon API Gateway (REST API) から Amazon EventBridge にリクエストを流すように直接統合する構成を AWS SAM (AWS CloudFormation) と OpenAPI で実装する検証をしていたので紹介する❗️ハマりどころもあって,困ったときに参考になるドキュメントも少なく,まとめておこうと思った.誰かの参考になれば💡

アーキテクチャ図

Amazon API Gateway (REST API) の /eventsPOST リクエストを投げると Amazon EventBridge を経由して Amazon CloudWatch Logs に流れるように構築した.Amazon CloudWatch Logs のところは何でも良くて,今回は簡単に疎通確認できるものを選んだ❗️

ディレクトリ構成

検証環境として最低限必要なディレクトリ構成は以下!

$ tree .
.
├── openapi
│   └── openapi.yaml
├── samconfig.toml
└── template.yaml

2 directories, 3 files

template.yaml

AWS SAM テンプレート(AWS CloudFormation テンプレート)で構築しているリソースはザッと以下になる.

  • Amazon API Gateway
  • Amazon EventBridge
  • Amazon CloudWatch Logs

ポイントは Amazon API Gateway の API 定義を OpenAPI を使って openapi.yaml ファイルから読む込むようにしてるところ👌

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Resources:
  ApiGateway:
    Type: AWS::Serverless::Api
    Properties:
      Name: sandbox-api-gateway
      StageName: v1
      DefinitionUri: ./openapi/openapi.yaml

  ApiGatewayExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: sandbox-api-gateway-execution-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - apigateway.amazonaws.com
            Action: sts:AssumeRole
      Path: '/'
      Policies:
        - PolicyName: ExecutionPolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - events:PutEvents
                Resource: '*'

  EventBus:
    Type: AWS::Events::EventBus
    Properties:
      Name: sandbox-event-bus

  EventRule:
    Type: AWS::Events::Rule
    Properties:
      EventBusName: !Ref EventBus
      EventPattern:
        source:
          - sandbox-source
        detail-type:
          - sandbox-detail-type
      Targets:
        - Id: sandbox-log-group
          Arn: !GetAtt LogGroup.Arn

  LogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: /aws/events/sandbox-log-group

openapi/openapi.yaml

次に OpenAPI ファイル(最低限の設定)を載せる❗️今回は /eventsPOST リクエストを送る API にした.

openapi: 3.0.0

info:
  title: Sandbox API
  version: 1.0.0

paths:
  /events:
    post:
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                event1:
                  type: string
                event2:
                  type: string
                event3:
                  type: string
              required:
                - event1
                - event2
                - event3
      x-amazon-apigateway-integration:
        type: aws
        uri: arn:aws:apigateway:${AWS::Region}:events:action/PutEvents
        httpMethod: POST
        credentials: arn:aws:iam::${AWS::AccountId}:role/sandbox-api-gateway-execution-role
        responses:
          default:
            statusCode: 200
        requestTemplates:
          application/json: "#set($context.requestOverride.header.X-Amz-Target = \"\
            AWSEvents.PutEvents\")\n#set($context.requestOverride.header.Content-Type\
            \ = \"application/x-amz-json-1.1\")\n{\n\t\"Entries\": [{\n\t\t\"EventBusName\"\
            : \"sandbox-event-bus\",\n\t\t\"Detail\": \"$util.escapeJavaScript($input.json('$'))\"\
            ,\n\t\t\"DetailType\": \"sandbox-detail-type\",\n\t\t\"Source\": \"sandbox-source\"\
            \n\t}]\n}"
        passthroughBehavior: when_no_templates
      responses:
        '200':
          description: OK

1番のハマりどころは拡張構文 x-amazon-apigateway-integration を使った記述と Amazon API Gateway のマッピングテンプレート回りだった.実際に動作確認できるまで何度も試行錯誤をした🌀

docs.aws.amazon.com

マッピングテンプレートに関しては Amazon API Gateway コンソールに表示される整形された形だと以下となる.OpenAPI ファイルに載せているエスケープされた形は Amazon API Gateway のエクスポート機能(OpenAPI 3 + API Gateway 拡張の形式でエクスポート)で取得したものをそのまま貼った.以下のドキュメントを参考に VTL (Apache Velocity Template Language) を実装して Amazon EventBridge にリクエストを流すように設定した.

docs.aws.amazon.com

#set($context.requestOverride.header.X-Amz-Target = "AWSEvents.PutEvents")
#set($context.requestOverride.header.Content-Type = "application/x-amz-json-1.1")
{
    "Entries": [{
        "EventBusName": "sandbox-event-bus",
        "Detail": "$util.escapeJavaScript($input.json('$'))",
        "DetailType": "sandbox-detail-type",
        "Source": "sandbox-source"
    }]
}

HTTP ヘッダー X-Amz-TargetAWSEvents.PutEvents を設定し,Content-Typeapplication/x-amz-json-1.1 を設定するところは Amazon EventBridge の PutEvents API リファレンスを参考にした.

docs.aws.amazon.com

動作確認

iTerm2 から Amazon API Gateway エンドポイントに POST リクエストを送る❗️期待通りに Amazon EventBridge の PutEvents API からレスポンスが返ってくる.

$ curl -s -X POST \
  -H 'Content-Type: application/json' \
  -d '{"event1": "event1", "event2": "event2", "event3": "event3"}' \
  https://dxqv7m4o28.execute-api.ap-northeast-1.amazonaws.com/v1/events | jq .
{
  "Entries": [
    {
      "EventId": "b6673a3d-b591-cfde-5ea1-208c77db482a"
    }
  ],
  "FailedEntryCount": 0
}

Amazon API Gateway (REST API) → Amazon EventBridge → Amazon CloudWatch Logs と流れて最終的にイベントをログ出力できた✌️

Amazon API Gateway コンソール

構築するときは Amazon API Gateway コンソールを使って動作確認をすることもあると思う.以下にキャプチャを載せておく❗️AWS サービスで EventBridge を選べず CloudWatch Events を選ぶところは要注意〜

関連記事

マッピングテンプレートの設定で参考になるドキュメントが少なく,以下のブログ記事 Capturing client events using Amazon API Gateway and Amazon EventBridge と GitHub リポジトリが1番参考になった.イイ事例だからブログ記事は日本語に翻訳してもらえると良いなぁ💡

aws.amazon.com

github.com

まとめ

Amazon API Gateway (REST API) から Amazon EventBridge にリクエストを流すように直接統合する構成を AWS SAM (AWS CloudFormation) と OpenAPI で実装してみた❗️

参考になれば💡

github.com