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


Parameters:
  Environment:
    Type: String
    Default: dev
    Description: Environment name
  LogLevel:
    Type: String
    Default: INFO
  RetentionInDays:
    Type: Number
    Default: 30
    Description: CloudWatch Logs retention period for Lambda functions
  EventBusName:
    Type: AWS::SSM::Parameter::Value<String>
    Description: EventBridge Event Bus Name
  Payment3PApiUrl:
    Type: AWS::SSM::Parameter::Value<String>
    Description: 3rd Party Payment API Gateway URL


Globals:
  Function:
    Runtime: python3.9
    Architectures:
      - arm64
    Handler: main.handler
    Timeout: 30
    Tracing: Active
    Environment:
      Variables:
        ENVIRONMENT: !Ref Environment
        TABLE_NAME: !Ref Table
        API_URL: !Ref Payment3PApiUrl
        POWERTOOLS_SERVICE_NAME: payment
        POWERTOOLS_TRACE_DISABLED: "false"
        LOG_LEVEL: !Ref LogLevel
    Layers:
      - !Sub "arn:aws:lambda:${AWS::Region}:580247275435:layer:LambdaInsightsExtension-Arm64:1"

Resources:
  #########
  # TABLE #
  #########
  Table:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: orderId
          AttributeType: S
      BillingMode: PAY_PER_REQUEST
      KeySchema:
        - AttributeName: orderId
          KeyType: HASH

  TableParameter:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub /ecommerce/${Environment}/payment/table/name
      Type: String
      Value: !Ref Table

  #############
  # FUNCTIONS #
  #############
  OnCompletedFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/on_completed/
      Events:
        DeliveryCompleted:
          Type: CloudWatchEvent
          Properties:
            EventBusName: !Ref EventBusName
            Pattern:
              source: [ecommerce.delivery]
              detail-type:
                - DeliveryCompleted
      EventInvokeConfig:
        DestinationConfig:
          OnFailure:
            Type: SQS
            Destination: !GetAtt DeadLetterQueue.Outputs.QueueArn
      Policies:
        - arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - dynamodb:DeleteItem
                - dynamodb:GetItem
              Resource:
                - !GetAtt Table.Arn

  OnCompletedLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${OnCompletedFunction}"
      RetentionInDays: !Ref RetentionInDays

  OnCreatedFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/on_created/
      Events:
        OrderCreated:
          Type: CloudWatchEvent
          Properties:
            EventBusName: !Ref EventBusName
            Pattern:
              source: [ecommerce.orders]
              detail-type:
                - OrderCreated
      EventInvokeConfig:
        DestinationConfig:
          OnFailure:
            Type: SQS
            Destination: !GetAtt DeadLetterQueue.Outputs.QueueArn          
      Policies:
        - arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action: dynamodb:PutItem
              Resource:
                - !GetAtt Table.Arn

  OnCreatedLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${OnCreatedFunction}"
      RetentionInDays: !Ref RetentionInDays

  OnFailedFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/on_failed/
      Events:
        PackagingFailed:
          Type: CloudWatchEvent
          Properties:
            EventBusName: !Ref EventBusName
            Pattern:
              source: [ecommerce.warehouse]
              detail-type:
                - PackagingFailed
        DeliveryFailed:
          Type: CloudWatchEvent
          Properties:
            EventBusName: !Ref EventBusName
            Pattern:
              source: [ecommerce.delivery]
              detail-type:
                - DeliveryFailed
      EventInvokeConfig:
        DestinationConfig:
          OnFailure:
            Type: SQS
            Destination: !GetAtt DeadLetterQueue.Outputs.QueueArn
      Policies:
        - arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - dynamodb:DeleteItem
                - dynamodb:GetItem
              Resource:
                - !GetAtt Table.Arn

  OnFailedLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${OnFailedFunction}"
      RetentionInDays: !Ref RetentionInDays

  OnModifiedFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/on_modified/
      Events:
        OrderModified:
          Type: CloudWatchEvent
          Properties:
            EventBusName: !Ref EventBusName
            Pattern:
              source: [ecommerce.orders]
              detail-type:
                - OrderModified
              detail:
                changed:
                  - total
      EventInvokeConfig:
        DestinationConfig:
          OnFailure:
            Type: SQS
            Destination: !GetAtt DeadLetterQueue.Outputs.QueueArn
      Policies:
        - arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action: dynamodb:GetItem
              Resource:
                - !GetAtt Table.Arn
  
  OnModifiedLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${OnModifiedFunction}"
      RetentionInDays: !Ref RetentionInDays

  ValidateFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: src/validate/
      Events:
        BackendApi:
          Type: Api
          Properties:
            Path: /backend/validate
            Method: POST
            RestApiId: !Ref Api
      Policies:
        - arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy

  ValidateLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lambda/${ValidateFunction}"
      RetentionInDays: !Ref RetentionInDays

  ###############
  # API GATEWAY #
  ###############
  Api:
    Type: AWS::Serverless::Api
    Properties:
      DefinitionBody:
        Fn::Transform:
          Name: "AWS::Include"
          Parameters:
            Location: "resources/openapi.yaml"
      EndpointConfiguration: REGIONAL
      StageName: prod
      TracingEnabled: true

  ApiUrlParameter:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub /ecommerce/${Environment}/payment/api/url
      Type: String
      Value: !Sub "https://${Api}.execute-api.${AWS::Region}.amazonaws.com/prod"

  ApiArnParameter:
    Type: AWS::SSM::Parameter
    Properties:
      Name: !Sub /ecommerce/${Environment}/payment/api/arn
      Type: String
      Value: !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${Api}/prod"

  #####################
  # DEAD LETTER QUEUE #
  #####################
  DeadLetterQueue:
    Type: AWS::CloudFormation::Stack
    Properties:
      # The path starts with '../..' as this will be evaluated from the
      # payment/build folder, not the payment folder.
      TemplateURL: ../../shared/templates/dlq.yaml

 #############
  # DASHBOARD #
  #############
  Dashboard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardName: !Ref AWS::StackName
      DashboardBody: !Sub |
        {
          "start": "-PT6H",
          "periodOverride": "inherit",
          "widgets": [
            {
              "type": "text",
              "x": 0,
              "y": 0,
              "width": 21,
              "height": 1,
              "properties": {
                "markdown": "\n# Key Metrics\n"
              }
            },
            {
              "type": "text",
              "x": 21,
              "y": 0,
              "width": 3,
              "height": 7,
              "properties": {
                "markdown": "\n## Useful links\n\n__CloudWatch Logs Insights queries__:\n\n* [Last 100 warning messages](https://${AWS::Region}.console.aws.amazon.com/cloudwatch/home?region=${AWS::Region}#logs-insights:queryDetail=~\\(end~0~start~-21600~timeType~'RELATIVE~unit~'seconds~editorString~'fields*20timestamp*2c*20level*2c*20message.orderId*20as*20orderId*2c*20message.paymentToken*20as*20paymentToken*2c*20message.message*20as*20message*0a*7c*20filter*20level*20*3d*3d*20*22WARNING*22*0a*7c*20sort*20*40timestamp*20desc*0a*7c*20limit*2020*0a~isLiveTail~false~queryId~'38d167e2-e680-4dc3-80fc-411dbd93a317~source~(~'*2faws*2flambda*2f${OnCreatedFunction}~'*2faws*2flambda*2f${OnModifiedFunction}~'*2faws*2flambda*2f${OnFailedFunction}~'*2faws*2flambda*2f${OnCompletedFunction}~'*2faws*2flambda*2f${ValidateFunction})\\))\n\n__Other links__:\n\n* [Repository](https://github.com/aws-samples/aws-serverless-ecommerce-platform/tree/main/payment)\n"
              }
            },
            {
              "type": "metric",
              "x": 0,
              "y": 1,
              "width": 8,
              "height": 6,
              "properties": {
                "metrics": [
                  ["ecommerce.payment", "amountWon", "environment", "${Environment}", "service", "payment", {"color": "#66bb6a", "label": "Amount Processed ($)"}],
                  ["ecommerce.payment", "amountLost", "environment", "${Environment}", "service", "payment", {"color": "#d62728", "label": "Amount Cancelled ($)"}],
                  ["ecommerce.payment", "paymentProcessed", "environment", "${Environment}", "service", "payment", {"color": "#dbdb8d", "label": "Payments Processed"}],
                  ["ecommerce.payment", "paymentCancelled", "environment", "${Environment}", "service", "payment", {"color": "#ff7f0e", "label": "Payments Cancelled"}],
                  ["ecommerce.payment", "paymentCreated", "environment", "${Environment}", "service", "payment", {"color": "#29b6f6", "label": "Payments Created"}]
                ],
                "view": "singleValue",
                "period": 86400,
                "stacked": false,
                "stat": "Sum",
                "region": "${AWS::Region}",
                "title": "Payments"
              }
            },
            {
              "type": "metric",
              "x": 8,
              "y": 1,
              "width": 7,
              "height": 6,
              "properties": {
                "metrics": [
                  [{"expression": "le1+le2+le3+le4+le5+lt1+lt2+lt3+lt4+lt5", "label": "Lambda Errors", "color": "#66bb6a"}],
                  [{"expression": "ag1", "label": "API Gateway 5XX", "color": "#ef5350"}],
                  [{"expression": "db1+db2", "label": "DynamoDB Errors", "color": "#ffa726"}],


                  ["AWS/Lambda", "Errors", "FunctionName", "${OnCreatedFunction}", {"id": "le1", "visible": false}],
                  ["AWS/Lambda", "Errors", "FunctionName", "${OnCompletedFunction}", {"id": "le2", "visible": false}],
                  ["AWS/Lambda", "Errors", "FunctionName", "${OnModifiedFunction}", {"id": "le3", "visible": false}],
                  ["AWS/Lambda", "Errors", "FunctionName", "${OnFailedFunction}", {"id": "le4", "visible": false}],
                  ["AWS/Lambda", "Errors", "FunctionName", "${ValidateFunction}", {"id": "le5", "visible": false}],

                  ["AWS/Lambda", "Throttles", "FunctionName", "${OnCreatedFunction}", {"id": "lt1", "visible": false}],
                  ["AWS/Lambda", "Throttles", "FunctionName", "${OnCompletedFunction}", {"id": "lt2", "visible": false}],
                  ["AWS/Lambda", "Throttles", "FunctionName", "${OnModifiedFunction}", {"id": "lt3", "visible": false}],
                  ["AWS/Lambda", "Throttles", "FunctionName", "${OnFailedFunction}", {"id": "lt4", "visible": false}],
                  ["AWS/Lambda", "Throttles", "FunctionName", "${ValidateFunction}", {"id": "lt5", "visible": false}],

                  ["AWS/ApiGateway", "5XXError", "ApiName", "${AWS::StackName}-api", {"id": "ag1", "visible": false}],

                  ["AWS/DynamoDB", "UserErrors", "TableName", "${Table}", {"id": "db1", "visible": false}],
                  ["AWS/DynamoDB", "SystemErrors", "TableName", "${Table}", {"id": "db2", "visible": false}]
                ],
                "view": "singleValue",
                "period": 86400,
                "stacked": false,
                "stat": "Sum",
                "region": "${AWS::Region}",
                "title": "Errors"
              }
            },
            {
              "type": "metric",
              "x": 15,
              "y": 1,
              "width": 6,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Duration", "FunctionName", "${ValidateFunction}", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "singleValue",
                "period": 86400,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "Validate Latency"
              }
            },
            {
              "type": "text",
              "x": 0,
              "y": 7,
              "width": 24,
              "height": 1,
              "properties": {
                "markdown": "\n# Traffic\n"
              }
            },
            {
              "type": "metric",
              "x": 0,
              "y": 8,
              "width": 12,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Invocations", "FunctionName", "${OnCreatedFunction}", {"color": "#29b6f6", "label": "CreatePayment"}],
                  ["AWS/Lambda", "Invocations", "FunctionName", "${OnCompletedFunction}", {"color": "#66bb6a", "label": "CompletePayment"}],
                  ["AWS/Lambda", "Invocations", "FunctionName", "${OnFailedFunction}", {"color": "#ec407a", "label": "CancelPayment"}],
                  ["AWS/Lambda", "Invocations", "FunctionName", "${OnModifiedFunction}", {"color": "#b300b3", "label": "ModifyPayment"}],
                  ["AWS/Lambda", "Invocations", "FunctionName", "${ValidateFunction}", {"color": "#ffa726", "label": "ValidatePayment"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "Sum",
                "region": "${AWS::Region}",
                "title": "Requests"
              }
            },
            {
              "type": "metric",
              "x": 12,
              "y": 8,
              "width": 12,
              "height": 6,
              "properties": {
                "metrics": [
                  ["ecommerce.payment", "paymentCreated", "environment", "${Environment}", "service", "payment", {"color": "#29b6f6", "label": "Payment Created"}],
                  ["ecommerce.payment", "paymentProcessed", "environment", "${Environment}", "service", "payment", {"color": "#66bb6a", "label": "Payment Processed"}],
                  ["ecommerce.payment", "paymentCancelled", "environment", "${Environment}", "service", "payment", {"color": "#ef5350", "label": "Payment Cancelled"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "Sum",
                "region": "${AWS::Region}",
                "title": "Payment counts"
              }
            },
            {
              "type": "metric",
              "x": 0,
              "y": 14,
              "width": 24,
              "height": 6,
              "properties": {
                "metrics": [
                  ["ecommerce.payment", "amountWon", "environment", "${Environment}", "service", "payment", {"color": "#66bb6a", "label": "Amount Processed ($)"}],
                  ["ecommerce.payment", "amountLost", "environment", "${Environment}", "service", "payment", {"color": "#ef5350", "label": "Amount Cancelled ($)"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "Sum",
                "region": "${AWS::Region}",
                "title": "Payment amounts"
              }
            },

            {
              "type": "text",
              "x": 0,
              "y": 20,
              "width": 24,
              "height": 1,
              "properties": {
                "markdown": "\n# Latency and Duration\n"
              }
            },
            {
              "type": "metric",
              "x": 0,
              "y": 21,
              "width": 8,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Duration", "FunctionName", "${OnCreatedFunction}", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "CreatePayment Duration"
              }
            },
            {
              "type": "metric",
              "x": 8,
              "y": 21,
              "width": 8,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Duration", "FunctionName", "${OnCompletedFunction}", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "CompletePayment Duration"
              }
            },
            {
              "type": "metric",
              "x": 16,
              "y": 21,
              "width": 8,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Duration", "FunctionName", "${OnFailedFunction}", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "CancelPayment Duration"
              }
            },
            {
              "type": "metric",
              "x": 0,
              "y": 27,
              "width": 12,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/ApiGateway", "Latency", "ApiName", "${AWS::StackName}-api", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "API Latency"
              }
            },
            {
              "type": "metric",
              "x": 12,
              "y": 27,
              "width": 12,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/DynamoDB", "SuccessfulRequestLatency", "TableName", "${Table}", "Operation", "GetItem", {"color": "#66bb6a", "label": "GetItem"}],
                  ["AWS/DynamoDB", "SuccessfulRequestLatency", "TableName", "${Table}", "Operation", "PutItem", {"color": "#ffa726", "label": "PutItem"}],
                  ["AWS/DynamoDB", "SuccessfulRequestLatency", "TableName", "${Table}", "Operation", "UpdateItem", {"color": "#29b6f6", "label": "UpdateItem"}],
                  ["AWS/DynamoDB", "SuccessfulRequestLatency", "TableName", "${Table}", "Operation", "DeleteItem", {"color": "#ef5350", "label": "DeleteItem"}],
                  ["AWS/DynamoDB", "SuccessfulRequestLatency", "TableName", "${Table}", "Operation", "Query", {"color": "#ec407a", "label": "Query"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "Average",
                "region": "${AWS::Region}",
                "title": "DynamoDB Latency"
              }
            },
            {
              "type": "metric",
              "x": 0,
              "y": 33,
              "width": 12,
              "height": 3,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Duration", "FunctionName", "${OnCreatedFunction}", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "singleValue",
                "period": 86400,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "OnCreated Latency"
              }
            },
            {
              "type": "metric",
              "x": 12,
              "y": 33,
              "width": 12,
              "height": 3,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Duration", "FunctionName", "${OnCompletedFunction}", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "singleValue",
                "period": 86400,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "OnCompleted Latency"
              }
            },
            {
              "type": "metric",
              "x": 0,
              "y": 36,
              "width": 12,
              "height": 3,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Duration", "FunctionName", "${OnModifiedFunction}", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "singleValue",
                "period": 86400,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "OnModified Latency"
              }
            },
            {
              "type": "metric",
              "x": 12,
              "y": 36,
              "width": 12,
              "height": 3,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Duration", "FunctionName", "${OnFailedFunction}", {"color": "#9ccc65", "label": "p50"}],
                  ["...", {"stat": "p90", "color": "#ffee58", "label": "p90"}],
                  ["...", {"stat": "p99", "color": "#ef5350", "label": "p99"}]
                ],
                "view": "singleValue",
                "period": 86400,
                "stacked": false,
                "stat": "p50",
                "region": "${AWS::Region}",
                "title": "OnFailed Latency"
              }
            },

            {
              "type": "text",
              "x": 0,
              "y": 39,
              "width": 24,
              "height": 1,
              "properties": {
                "markdown": "\n# Errors\n"
              }
            },
            {
              "type": "metric",
              "x": 0,
              "y": 40,
              "width": 8,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/Lambda", "Errors", "FunctionName", "${OnCreatedFunction}", {"color": "#29b6f6", "label": "CreatePayment"}],
                  ["AWS/Lambda", "Errors", "FunctionName", "${OnCompletedFunction}", {"color": "#66bb6a", "label": "CompletePayment"}],
                  ["AWS/Lambda", "Errors", "FunctionName", "${OnFailedFunction}", {"color": "#ec407a", "label": "CancelPayment"}],
                  ["AWS/Lambda", "Errors", "FunctionName", "${OnModifiedFunction}", {"color": "#b300b3", "label": "ModifyPayment"}],
                  ["AWS/Lambda", "Errors", "FunctionName", "${ValidateFunction}", {"color": "#ffa726", "label": "ValidatePayment"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "Sum",
                "region": "${AWS::Region}",
                "title": "Lambda Errors"
              }
            },
            {
              "type": "metric",
              "x": 8,
              "y": 40,
              "width": 8,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/ApiGateway", "4XXError", "ApiName", "${AWS::StackName}-api", {"color": "#ffa726", "label": "4XX Errors"}],
                  ["AWS/ApiGateway", "5XXError", "ApiName", "${AWS::StackName}-api", {"color": "#ef5350", "label": "5XX Errors"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "Sum",
                "region": "${AWS::Region}",
                "title": "API Errors"
              }
            },
            {
              "type": "metric",
              "x": 16,
              "y": 40,
              "width": 8,
              "height": 6,
              "properties": {
                "metrics": [
                  ["AWS/DynamoDB", "UserErrors", "TableName", "${Table}", {"color": "#ffa726", "label": "User Errors"}],
                  ["AWS/DynamoDB", "SystemErrors", "TableName", "${Table}", {"color": "#ef5350", "label": "System Errors"}]
                ],
                "view": "timeSeries",
                "period": 60,
                "stacked": false,
                "stat": "Sum",
                "region": "${AWS::Region}",
                "title": "DynamoDB Errors"
              }
            },
            {
              "type": "text",
              "x": 0,
              "y": 46,
              "width": 24,
              "height": 1,
              "properties": {
                "markdown": "\n# Logs\n"
              }
            },
            {
              "type": "log",
              "x": 0,
              "y": 47,
              "width": 24,
              "height": 6,
              "properties": {
                "query": "SOURCE '/aws/lambda/${OnCreatedFunction}' | SOURCE '/aws/lambda/${OnCompletedFunction}' | SOURCE '/aws/lambda/${OnFailedFunction}' | SOURCE '/aws/lambda/${OnModifiedFunction}' | SOURCE '/aws/lambda/${ValidateFunction}' | fields timestamp, level, message.orderId as orderId, message.message as message, message.paymentToken as paymentToken \n| PARSE @message \"[*] *\" as loggingLevel, loggingMessage \n| filter (loggingLevel == \"ERROR\" or level == \"ERROR\")\n| sort @timestamp desc\n| limit 20\n",
                "region": "${AWS::Region}",
                "stacked": false,
                "view": "table",
                "title": "Last 20 errors"
              }
            }
          ]
        }