handlers/product-move-api/cfn.yaml (564 lines of code) (raw):
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