AWSTemplateFormatVersion: '2010-09-09'
Description: Endpoints to handle product movement, initially recurring contribution
  to digi sub
Parameters:
  DeployBucket:
    Description: Bucket to copy files to
    Type: String
    Default: support-service-lambdas-dist
  Stack:
    Description: Stack name
    Type: String
    Default: membership
  Stage:
    Description: Set by RiffRaff on each deploy
    Type: String
    AllowedValues:
      - CODE
      - PROD

Mappings:
  StageMap:
    PROD:
      DomainName: product-move-api.support.guardianapis.com
    CODE:
      DomainName: product-move-api-code.support.guardianapis.com

Resources:
  ProductMovementFailureAlarm:
    Type: AWS::CloudWatch::Alarm
    Condition: IsProd
    Properties:
      AlarmActions:
        - Fn::Sub: arn:aws:sns:${AWS::Region}:${AWS::AccountId}:alarms-handler-topic-${Stage}
      AlarmName:
        Fn::Sub: ${Stage} An error in the Product Move lambda. Please check the logs to diagnose
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value:
            Ref: ProductMoveApiLambda
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 300
      Statistic: Sum
      Threshold: 1
      TreatMissingData: notBreaching
      Tags:
        - Key: DiagnosticLinks
          Value:
            Fn::Sub: lambda:move-product-${Stage}

  SQSTriggerSF:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: true
      EventSourceArn:
        Fn::GetAtt: SalesforceTrackingQueue.Arn
      FunctionName:
        Ref: SalesforceTrackingLambda

  ProductMoveApiLambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket:
          Ref: DeployBucket
        S3Key:
          Fn::Sub: "${Stack}/${Stage}/product-move-api/product-move-api.jar"
      Description: A lambda for handling product movement API requests
      FunctionName:
        Fn::Sub: move-product-${Stage}
      Handler: com.gu.productmove.Handler::handleRequest
      MemorySize: 6144
      Role:
        Fn::GetAtt:
          - ProductMoveApiLambdaRole
          - Arn
      Runtime: java21
      Timeout: 300
      Environment:
        Variables:
          App: product-move-api
          Stack:
            Ref: Stack
          Stage:
            Ref: Stage
          EmailQueueName:
            Fn::ImportValue:
              Fn::Sub: comms-${Stage}-EmailQueueName
      Tags:
        - Key: lambda:createdBy
          Value: SAM
      Architectures:
        - arm64
  RefundDeadLetterQueue:
    Type: AWS::SQS::Queue
    Properties:
      MessageRetentionPeriod: 1209600 # 14 days this needs to be longer than the message retention period of the main queue because the expiration of a message is always based on its original enqueue timestamp
      QueueName:
        Fn::Sub: product-switch-refund-dead-letter-${Stage}
  ProductMoveApiGatewayUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    DependsOn:
      - ProductMoveApiGateway
    Properties:
      ApiStages:
        - ApiId:
            Ref: ProductMoveApiGateway
          Stage:
            Ref: ProductMoveApiGatewayStage
      Description:
        Fn::Sub: Usage plan for product-move-api-PROD
      UsagePlanName:
        Fn::Sub: product-move-api-PROD-UsagePlan
  ProductMoveApiLambdaDocsPermissionStage:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Ref: ProductMoveApiLambda
      Principal: apigateway.amazonaws.com
      SourceArn:
        Fn::Sub:
          - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/docs/*
          - __ApiId__:
              Ref: ProductMoveApiGateway
            __Stage__: "*"
  ProductMoveApiLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: ProductMoveApiLambdaRolePolicy0
          PolicyDocument:
            Statement:
              Effect: Allow
              Action:
                - sqs:GetQueueUrl
                - sqs:SendMessage
              Resource:
                - Fn::ImportValue:
                    Fn::Sub: comms-${Stage}-EmailQueueArn
                - Fn::GetAtt: RefundQueue.Arn
                - Fn::GetAtt: SalesforceTrackingQueue.Arn
        - PolicyName: ProductMoveApiLambdaRolePolicy2
          PolicyDocument:
            Statement:
              Effect: Allow
              Action: s3:GetObject
              Resource:
                - Fn::Sub: arn:aws:s3:::gu-reader-revenue-private/membership/support-service-lambdas/${Stage}/invoicingApi-${Stage}*.json
                - Fn::Sub: arn:aws:s3:::gu-zuora-catalog/${Stage}/Zuora-${Stage}/catalog.json
                - arn:aws:s3::*:membership-dist/*
        - PolicyName: ProductMoveApiLambdaRolePolicy3
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - dynamodb:PutItem
                  - dynamodb:UpdateItem
                Resource:
                  - Fn::ImportValue:
                      Fn::Sub: supporter-product-data-tables-${Stage}-SupporterProductDataTable
        - PolicyName: ProductMoveApiLambdaRolePolicy4
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - secretsmanager:DescribeSecret
                  - secretsmanager:GetSecretValue
                Resource:
                  - arn:aws:secretsmanager:eu-west-1:865473395570:secret:CODE/InvoicingApi-qNhLQS
                  - arn:aws:secretsmanager:eu-west-1:865473395570:secret:PROD/InvoicingApi-JBxYpW
                  - arn:aws:secretsmanager:eu-west-1:865473395570:secret:CODE/Zuora/User/ZuoraApiUser-zmOGho
                  - arn:aws:secretsmanager:eu-west-1:865473395570:secret:PROD/Zuora/User/ZuoraApiUser-oq5ISm
      Tags:
        - Key: lambda:createdBy
          Value: SAM
  ProductMoveApiGatewayUsagePlanKey:
    Type: AWS::ApiGateway::UsagePlanKey
    DependsOn:
      - ProductMoveApiGatewayApiKey
    Properties:
      KeyId:
        Ref: ProductMoveApiGatewayApiKey
      KeyType: API_KEY
      UsagePlanId:
        Ref: ProductMoveApiGatewayUsagePlan
  ProductMoveApiLambdaProductMovementPermissionStage:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Ref: ProductMoveApiLambda
      Principal: apigateway.amazonaws.com
      SourceArn:
        Fn::Sub:
          - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/*
          - __ApiId__:
              Ref: ProductMoveApiGateway
            __Stage__: "*"
  SQSTrigger:
    Type: AWS::Lambda::EventSourceMapping
    Properties:
      BatchSize: 1
      Enabled: true
      EventSourceArn:
        Fn::GetAtt: RefundQueue.Arn
      FunctionName:
        Ref: RefundLambda
  ProductMoveApiGatewayDeployment3b8d6b60f7:
    Type: AWS::ApiGateway::Deployment
    Properties:
      Description: 'RestApi deployment id: 3b8d6b60f7fc8b38903c9cf0441d50831c0b59db'
      RestApiId:
        Ref: ProductMoveApiGateway
  SalesforceTrackingQueue:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 3000
      QueueName:
        Fn::Sub: product-switch-salesforce-tracking-${Stage}
  SalesforceTrackingLambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket:
          Ref: DeployBucket
        S3Key:
          Fn::Sub: "${Stack}/${Stage}/product-move-api/product-move-api.jar"
      Description: An SQS-triggered lambda that refunds customer's going through product-switching
      FunctionName:
        Fn::Sub: product-switch-salesforce-tracking-${Stage}
      Handler: com.gu.productmove.salesforce.SalesforceHandler::handleRequest
      MemorySize: 1024
      Role:
        Fn::GetAtt:
          - SalesforceTrackingLambdaRole
          - Arn
      Runtime: java21
      Timeout: 300
      Environment:
        Variables:
          App: product-move-api
          Stack:
            Ref: Stack
          Stage:
            Ref: Stage
      Tags:
        - Key: lambda:createdBy
          Value: SAM
      Architectures:
        - arm64
  ProductMoveApiGateway:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Body:
        info:
          version: '1.0'
          title:
            Ref: AWS::StackName
        paths:
          "/{proxy+}":
            x-amazon-apigateway-any-method:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProductMoveApiLambda.Arn}/invocations
              security:
                - api_key: []
              responses: {}
          "/docs/{proxy+}":
            x-amazon-apigateway-any-method:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProductMoveApiLambda.Arn}/invocations
              security: []
              responses: {}
          "/docs":
            x-amazon-apigateway-any-method:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ProductMoveApiLambda.Arn}/invocations
              security: []
              responses: {}
        openapi: 3.0.1
        components:
          securitySchemes:
            api_key:
              type: apiKey
              name: x-api-key
              in: header
      Name:
        Fn::Sub: product-move-api-${Stage}-ApiGateway
  RefundQueue:
    Type: AWS::SQS::Queue
    Properties:
      VisibilityTimeout: 43200 # 12 hours
      MessageRetentionPeriod: 606600 # 7 days +30 minutes
      QueueName:
        Fn::Sub: product-switch-refund-${Stage}
      RedrivePolicy:
        deadLetterTargetArn:
          Fn::GetAtt: RefundDeadLetterQueue.Arn
        maxReceiveCount: 14
  RefundLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: RefundLambdaRolePolicy1
          PolicyDocument:
            Statement:
              Effect: Allow
              Action: s3:GetObject
              Resource:
                - Fn::Sub: arn:aws:s3:::gu-reader-revenue-private/membership/support-service-lambdas/${Stage}/zuoraRest-${Stage}*.json
                - Fn::Sub: arn:aws:s3:::gu-reader-revenue-private/membership/support-service-lambdas/${Stage}/invoicingApi-${Stage}*.json
                - arn:aws:s3::*:membership-dist/*
        - PolicyName: RefundLambdaRolePolicy2
          PolicyDocument:
            Statement:
              Effect: Allow
              Action:
                - sqs:DeleteMessage
                - sqs:GetQueueAttributes
                - sqs:ReceiveMessage
              Resource: "*"
        - PolicyName: RefundLambdaRolePolicy3
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - secretsmanager:DescribeSecret
                  - secretsmanager:GetSecretValue
                Resource:
                  - arn:aws:secretsmanager:eu-west-1:865473395570:secret:CODE/InvoicingApi-qNhLQS
                  - arn:aws:secretsmanager:eu-west-1:865473395570:secret:PROD/InvoicingApi-JBxYpW
                  - arn:aws:secretsmanager:eu-west-1:865473395570:secret:CODE/Zuora/User/ZuoraApiUser-zmOGho
                  - arn:aws:secretsmanager:eu-west-1:865473395570:secret:PROD/Zuora/User/ZuoraApiUser-oq5ISm
      Tags:
        - Key: lambda:createdBy
          Value: SAM

  5xxApiAlarm:
    Type: AWS::CloudWatch::Alarm
    Condition: IsProd
    DependsOn:
      - ProductMoveApiGateway
    Properties:
      AlarmActions:
        - Fn::Sub: arn:aws:sns:${AWS::Region}:${AWS::AccountId}:alarms-handler-topic-${Stage}
      AlarmName:
        Fn::Sub: ${Stage} The product-move-api returned a 500 response
      AlarmDescription: Check the logs for details
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: ApiName
          Value:
            Fn::Sub: product-move-api-${Stage}-ApiGateway
        - Name: Stage
          Value:
            Fn::Sub: "${Stage}"
      EvaluationPeriods: 1
      MetricName: 5XXError
      Namespace: AWS/ApiGateway
      Period: 60
      Statistic: Sum
      Threshold: 1
      TreatMissingData: notBreaching
      Tags:
        - Key: DiagnosticLinks
          Value:
            Fn::Sub: lambda:move-product-${Stage}

  RefundLambdaDeadLetterQueueAlarm:
    Type: AWS::CloudWatch::Alarm
    Condition: IsProd
    Properties:
      AlarmActions:
        - Fn::Sub: arn:aws:sns:${AWS::Region}:${AWS::AccountId}:alarms-handler-topic-${Stage}
      AlarmName: New message in the product-switch-refund-dead-letter-PROD dead letter queue.
      AlarmDescription: There is a new message in the product-switch-refund-dead-letter-PROD dead letter queue. This means that a user who has cancelled their supporter plus subscription
        within 14 days has not received the refund that they are due.
        Please check the
        product-switch-refund-PROD logs - https://eu-west-1.console.aws.amazon.com/cloudwatch/home?region=eu-west-1#logsV2:log-groups/log-group/$252Faws$252Flambda$252Fproduct-switch-refund-PROD
        and the
        invoicing-api-refund-PROD logs - https://eu-west-1.console.aws.amazon.com/cloudwatch/home?region=eu-west-1#logsV2:log-groups/log-group/$252Faws$252Flambda$252Finvoicing-api-refund-PROD
        to diagnose the issue.
      Namespace: AWS/SQS
      MetricName: ApproximateNumberOfMessagesVisible
      Dimensions:
        - Name: QueueName
          Value: !GetAtt RefundDeadLetterQueue.QueueName
      Statistic: Sum
      Period: 60
      EvaluationPeriods: 1
      Threshold: 0
      ComparisonOperator: GreaterThanThreshold
      TreatMissingData: notBreaching
  ProductMoveApiLambdaDocsRedirectPermissionStage:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName:
        Ref: ProductMoveApiLambda
      Principal: apigateway.amazonaws.com
      SourceArn:
        Fn::Sub:
          - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${__ApiId__}/${__Stage__}/*/docs
          - __ApiId__:
              Ref: ProductMoveApiGateway
            __Stage__: "*"
  ProductMoveApiGatewayStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId:
        Ref: ProductMoveApiGatewayDeployment3b8d6b60f7
      RestApiId:
        Ref: ProductMoveApiGateway
      StageName:
        Fn::Sub: "${Stage}"
  RefundLambda:
    Type: AWS::Lambda::Function
    Properties:
      Code:
        S3Bucket:
          Ref: DeployBucket
        S3Key:
          Fn::Sub: "${Stack}/${Stage}/product-move-api/product-move-api.jar"
      Description: An SQS-triggered lambda that refunds customer's going through product-switching
      FunctionName:
        Fn::Sub: product-switch-refund-${Stage}
      Handler: com.gu.productmove.refund.RefundHandler::handleRequest
      MemorySize: 1024
      Role:
        Fn::GetAtt:
          - RefundLambdaRole
          - Arn
      Runtime: java21
      Timeout: 300
      Environment:
        Variables:
          App: product-move-api
          Stack:
            Ref: Stack
          Stage:
            Ref: Stage
      Tags:
        - Key: lambda:createdBy
          Value: SAM
      Architectures:
        - arm64
  ProductMoveApiGatewayApiKey:
    Type: AWS::ApiGateway::ApiKey
    DependsOn:
      - ProductMoveApiGatewayUsagePlan
    Properties:
      Enabled: true
      StageKeys:
        - RestApiId:
            Ref: ProductMoveApiGateway
          StageName:
            Ref: ProductMoveApiGatewayStage
  SalesforceTrackingLambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Action:
              - sts:AssumeRole
            Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: SalesforceTrackingLambdaRolePolicy1
          PolicyDocument:
            Statement:
              Effect: Allow
              Action: s3:GetObject
              Resource:
                - arn:aws:s3::*:membership-dist/*
        - PolicyName: SalesforceTrackingLambdaRolePolicy2
          PolicyDocument:
            Statement:
              Effect: Allow
              Action:
                - sqs:DeleteMessage
                - sqs:GetQueueAttributes
                - sqs:ReceiveMessage
              Resource: "*"
        - PolicyName: SalesforceTrackingLambdaRolePolicy3
          PolicyDocument:
            Statement:
              Effect: Allow
              Action:
                - secretsmanager:DescribeSecret
                - secretsmanager:GetSecretValue
              Resource:
                - arn:aws:secretsmanager:eu-west-1:865473395570:secret:CODE/Salesforce/User/SupportServiceLambdas-729iA5
                - arn:aws:secretsmanager:eu-west-1:865473395570:secret:PROD/Salesforce/User/SupportServiceLambdas-417yMt
                - arn:aws:secretsmanager:eu-west-1:865473395570:secret:CODE/Zuora/User/ZuoraApiUser-zmOGho
                - arn:aws:secretsmanager:eu-west-1:865473395570:secret:PROD/Zuora/User/ZuoraApiUser-oq5ISm
      Tags:
        - Key: lambda:createdBy
          Value: SAM

  ProductMoveApiDomainName:
    Type: "AWS::ApiGateway::DomainName"
    Properties:
      RegionalCertificateArn: # only for *.support.guardianapis.com
        !Sub arn:aws:acm:${AWS::Region}:${AWS::AccountId}:certificate/b384a6a0-2f54-4874-b99b-96eeff96c009
      DomainName: !FindInMap [ StageMap, !Ref Stage, DomainName ]
      EndpointConfiguration:
        Types:
          - REGIONAL

  ProductMoveApiBasePathMapping:
    Type: "AWS::ApiGateway::BasePathMapping"
    Properties:
      RestApiId: !Ref ProductMoveApiGateway
      DomainName: !Ref ProductMoveApiDomainName
      Stage: !Sub ${Stage}
    DependsOn:
      - ProductMoveApiGateway
      - ProductMoveApiDomainName
      - ProductMoveApiGatewayStage

  ProductMoveApiDNSRecord:
    Type: AWS::Route53::RecordSet
    Properties:
      HostedZoneName: support.guardianapis.com.
      Name: !FindInMap [ StageMap, !Ref Stage, DomainName ]
      Type: CNAME
      TTL: '120'
      ResourceRecords:
        - !GetAtt ProductMoveApiDomainName.RegionalDomainName

Conditions:
  IsProd:
    Fn::Equals:
      - Ref: Stage
      - PROD
