cdk/lib/facia-connection.ts (109 lines of code) (raw):

import type { GuParameter, GuStack } from '@guardian/cdk/lib/constructs/core'; import { GuLambdaFunction } from '@guardian/cdk/lib/constructs/lambda'; import { Duration } from 'aws-cdk-lib'; import { Effect, PolicyStatement, Role } from 'aws-cdk-lib/aws-iam'; import { Architecture, Runtime } from 'aws-cdk-lib/aws-lambda'; import { SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; import { Topic } from 'aws-cdk-lib/aws-sns'; import { SqsSubscription } from 'aws-cdk-lib/aws-sns-subscriptions'; import { Queue } from 'aws-cdk-lib/aws-sqs'; import { Construct } from 'constructs'; import type { ExternalParameters } from './external_parameters'; import type { StaticServing } from './static-serving'; interface FaciaConnectionProps { fastlyKeyParam: GuParameter; serving: StaticServing; externalParameters: ExternalParameters; faciaPublishStatusSNSRoleARN: string; faciaPublishStatusSNSTopicARN: string; faciaPublishSNSTopicARN: string; contentUrlBase: string; } export class FaciaConnection extends Construct { constructor( scope: GuStack, id: string, { faciaPublishSNSTopicARN, faciaPublishStatusSNSTopicARN, faciaPublishStatusSNSRoleARN, externalParameters, fastlyKeyParam, serving, contentUrlBase, }: FaciaConnectionProps, ) { super(scope, id); const faciaPublishStatusSNSTopic = Topic.fromTopicArn( this, 'faciaPublishStatusSNSTopic', faciaPublishStatusSNSTopicARN, ); const faciaPublishStatusSNSRole = Role.fromRoleArn( scope, 'faciaPublishStatusSNSTopicRole', faciaPublishStatusSNSRoleARN, ); const faciaPublishSNSTopic = Topic.fromTopicArn( this, 'faciaPublishSNSTopic', faciaPublishSNSTopicARN, ); const faciaDLQ = new Queue(this, 'DLQ'); const faciaQueue = new Queue(this, 'Connection', { enforceSSL: true, deadLetterQueue: { queue: faciaDLQ, maxReceiveCount: 1, }, }); faciaPublishSNSTopic.addSubscription(new SqsSubscription(faciaQueue)); new GuLambdaFunction(scope, 'RecipesFaciaResponder', { events: [ new SqsEventSource(faciaQueue, { batchSize: 1, //we are not expecting heavy traffic so just invoke as the records arrive, one-by-one maxConcurrency: 5, }), ], errorPercentageMonitoring: { toleratedErrorPercentage: 1, snsTopicName: externalParameters.nonUrgentAlarmTopicArn.stringValue, }, app: 'recipes-facia-responder', architecture: Architecture.ARM_64, environment: { FASTLY_API_KEY: fastlyKeyParam.valueAsString, STATIC_BUCKET: serving.staticBucket.bucketName, CONTENT_URL_BASE: contentUrlBase, FACIA_PUBLISH_STATUS_TOPIC_ARN: faciaPublishStatusSNSTopic.topicArn, FACIA_PUBLISH_STATUS_ROLE_ARN: faciaPublishStatusSNSRole.roleArn, }, fileName: 'facia-responder.zip', functionName: `RecipesFaciaResponder-${scope.stage}`, handler: 'main.handler', initialPolicy: [ new PolicyStatement({ effect: Effect.DENY, resources: [ serving.staticBucket.bucketArn + '/content/*', serving.staticBucket.bucketArn + '/index.json', ], actions: ['s3:*'], }), new PolicyStatement({ effect: Effect.ALLOW, resources: [serving.staticBucket.bucketArn + '/*'], actions: ['s3:PutObject', 's3:ListObjects'], }), new PolicyStatement({ effect: Effect.ALLOW, actions: ['sts:AssumeRole'], resources: [faciaPublishStatusSNSRole.roleArn], }), ], memorySize: 256, runtime: Runtime.NODEJS_20_X, timeout: Duration.seconds(10), }); } }