constructor()

in cdk/lib/ticket-tailor-webhook.ts [26:170]


	constructor(scope: App, id: string, props: TicketTailorWebhookProps) {
		super(scope, id, props);

		const app = 'ticket-tailor-webhook';
		const nameWithStage = `${app}-${this.stage}`;

		const alarmsTopic = `alarms-handler-topic-${this.stage}`;

		const commonEnvironmentVariables = {
			App: app,
			Stack: this.stack,
			Stage: this.stage,
		};

		// SQS
		const queueName = `${app}-queue-${props.stage}`;
		const deadLetterQueueName = `${app}-dlq-${props.stage}`;

		const deadLetterQueue = new Queue(this, deadLetterQueueName, {
			queueName: deadLetterQueueName,
			retentionPeriod: Duration.days(14),
		});

		const queue = new Queue(this, queueName, {
			queueName,
			deadLetterQueue: {
				// The number of times a message can be unsuccessfully dequeued before being moved to the dlq
				maxReceiveCount: 5,
				queue: deadLetterQueue,
			},
			// This must be >= the lambda timeout
			visibilityTimeout: Duration.minutes(5),
		});

		// SQS to Lambda event source mapping
		const eventSource = new SqsEventSource(queue, {
			reportBatchItemFailures: true,
		});
		const events = [eventSource];

		// grant sqs:SendMessage* to Api Gateway Role
		const apiRole = new Role(this, 'ApiGatewayToSqsRole', {
			assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
		});
		queue.grantSendMessages(apiRole);

		// API Gateway Direct Integration
		const sendMessageIntegration = new AwsIntegration({
			service: 'sqs',
			path: `${this.account}/${queue.queueName}`,
			integrationHttpMethod: 'POST',
			options: {
				credentialsRole: apiRole,
				requestParameters: {
					'integration.request.header.Content-Type':
						"'application/x-www-form-urlencoded'",
				},
				requestTemplates: {
					'application/json':
						'Action=SendMessage&MessageBody=$util.urlEncode($input.body)&MessageAttribute.1.Name=tickettailor-webhook-signature&MessageAttribute.1.Value.DataType=String&MessageAttribute.1.Value.StringValue=$method.request.header.tickettailor-webhook-signature',
				},
				integrationResponses: [
					{
						statusCode: '200',
						responseTemplates: {
							'application/json': '{ "status": "accepted" }',
						},
					},
				],
			},
		});

		// ---- API-triggered lambda functions ---- //
		const lambda = new GuLambdaFunction(this, `${app}-lambda`, {
			description:
				'An API Gateway triggered lambda generated in the support-service-lambdas repo',
			functionName: nameWithStage,
			fileName: `${app}.zip`,
			handler: 'index.handler',
			runtime: nodeVersion,
			memorySize: 1024,
			timeout: Duration.seconds(300),
			environment: commonEnvironmentVariables,
			app: app,
			events,
		});

		const prodMonitoringConfiguration = {
			snsTopicName: alarmsTopic,
			http5xxAlarm: {
				tolerated5xxPercentage: 0,
			},
		};
		const apiGateway = new GuApiGatewayWithLambdaByPath(this, {
			app,
			monitoringConfiguration:
				this.stage === 'CODE'
					? { noMonitoring: true }
					: prodMonitoringConfiguration,
			targets: [],
		});
		apiGateway.api.root
			.resourceForPath('/')
			.addMethod('POST', sendMessageIntegration, {
				methodResponses: [
					{
						statusCode: '200',
					},
				],
			});

		const s3InlinePolicy: Policy = new Policy(this, 'S3 inline policy', {
			statements: [
				new PolicyStatement({
					effect: Effect.ALLOW,
					actions: ['s3:GetObject'],
					resources: [
						`arn:aws:s3::*:membership-dist/${this.stack}/${this.stage}/${app}/`,
					],
				}),
			],
		});

		const secretManagerAccessPolicy = new Policy(
			this,
			'Secret manager access policy',
			{
				statements: [
					new PolicyStatement({
						actions: ['secretsmanager:GetSecretValue'],
						resources: [
							`arn:aws:secretsmanager:${this.region}:${this.account}:secret:${this.stage}/TicketTailor/Webhook-validation-*`,
							`arn:aws:secretsmanager:${this.region}:${this.account}:secret:${this.stage}/TicketTailor/IdApi-token-*`,
						],
					}),
				],
			},
		);

		const cloudwatchPutMetricPolicy = new GuPutCloudwatchMetricsPolicy(this);

		lambda.role?.attachInlinePolicy(s3InlinePolicy);
		lambda.role?.attachInlinePolicy(secretManagerAccessPolicy);
		lambda.role?.attachInlinePolicy(cloudwatchPutMetricPolicy);
	}