diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 913171a8..59a4cf10 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -56,6 +56,12 @@ jobs: npm run build zip -FSjr "dist/ingestion-lambda.zip" "dist/handler.js" + - name: Build poller-lambdas + working-directory: poller-lambdas + run: | + npm run build + zip -FSjr "dist/poller-lambdas.zip" "dist/index.js" + - name: Build Newswires app and UI working-directory: newswires run: | @@ -80,5 +86,7 @@ jobs: - cdk/cdk.out ingestion-lambda: - ingestion-lambda/dist/ingestion-lambda.zip + poller-lambdas: + - poller-lambdas/dist/poller-lambdas.zip newswires: - newswires/target/newswires.deb diff --git a/cdk/bin/cdk.ts b/cdk/bin/cdk.ts index c4903d43..377d9e8d 100644 --- a/cdk/bin/cdk.ts +++ b/cdk/bin/cdk.ts @@ -5,6 +5,7 @@ import { Newswires } from '../lib/newswires'; const app = new GuRoot(); +// FIXME we should probably have a dedicated stack for the Newswires app for e.g. cost explorer purposes (as the App tag needs to be different for riff-raff etc. to differentiate) const stack = 'editorial-feeds'; const env = { @@ -23,12 +24,15 @@ const prodWiresFeeds = new WiresFeeds(app, 'WiresFeeds-PROD', { stage: 'PROD', }); +// TODO find a way to make the Newswires stacks 'depend on' the WiresFeeds stacks in the generated riff-raff.yaml + new Newswires(app, 'Newswires-CODE', { env, stack, stage: 'CODE', domainName: 'newswires.code.dev-gutools.co.uk', enableMonitoring: false, + sourceQueue: codeWiresFeeds.sourceQueue, fingerpostQueue: codeWiresFeeds.fingerpostQueue, }); @@ -38,5 +42,6 @@ new Newswires(app, 'Newswires-PROD', { stage: 'PROD', domainName: 'newswires.gutools.co.uk', enableMonitoring: false, + sourceQueue: prodWiresFeeds.sourceQueue, fingerpostQueue: prodWiresFeeds.fingerpostQueue, }); diff --git a/cdk/lib/__snapshots__/newswires.test.ts.snap b/cdk/lib/__snapshots__/newswires.test.ts.snap index 747ade40..e9f9f50c 100644 --- a/cdk/lib/__snapshots__/newswires.test.ts.snap +++ b/cdk/lib/__snapshots__/newswires.test.ts.snap @@ -13,6 +13,8 @@ exports[`The Newswires stack matches the snapshot 1`] = ` "GuLambdaFunction", "GuParameter", "GuParameter", + "GuLambdaFunction", + "GuLambdaFunction", "GuGetS3ObjectsPolicy", "GuGetS3ObjectsPolicy", "GuSubnetListParameter", @@ -139,83 +141,795 @@ exports[`The Newswires stack matches the snapshot 1`] = ` "Value": { "Ref": "LoggingStreamName", }, - }, - { - "Key": "Stack", - "PropagateAtLaunch": true, - "Value": "editorial-feeds", - }, - { - "Key": "Stage", - "PropagateAtLaunch": true, - "Value": "TEST", - }, - { - "Key": "SystemdUnit", - "PropagateAtLaunch": true, - "Value": "newswires.service", - }, - ], - "TargetGroupARNs": [ - { - "Ref": "TargetGroupNewswires87809E8A", - }, - ], - "VPCZoneIdentifier": { - "Ref": "newswiresPrivateSubnets", + }, + { + "Key": "Stack", + "PropagateAtLaunch": true, + "Value": "editorial-feeds", + }, + { + "Key": "Stage", + "PropagateAtLaunch": true, + "Value": "TEST", + }, + { + "Key": "SystemdUnit", + "PropagateAtLaunch": true, + "Value": "newswires.service", + }, + ], + "TargetGroupARNs": [ + { + "Ref": "TargetGroupNewswires87809E8A", + }, + ], + "VPCZoneIdentifier": { + "Ref": "newswiresPrivateSubnets", + }, + }, + "Type": "AWS::AutoScaling::AutoScalingGroup", + }, + "CertificateNewswiresFEA3DE56": { + "DeletionPolicy": "Retain", + "Properties": { + "DomainName": "newswires.TEST.dev-gutools.co.uk", + "Tags": [ + { + "Key": "App", + "Value": "newswires", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/newswires", + }, + { + "Key": "Name", + "Value": "Newswires/CertificateNewswires", + }, + { + "Key": "Stack", + "Value": "editorial-feeds", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "ValidationMethod": "DNS", + }, + "Type": "AWS::CertificateManager::Certificate", + "UpdateReplacePolicy": "Retain", + }, + "DefaultSecurityGroupNewswires91CCC0BF": { + "Properties": { + "GroupDescription": "Newswires/DefaultSecurityGroupNewswires", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1", + }, + ], + "Tags": [ + { + "Key": "App", + "Value": "newswires", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/newswires", + }, + { + "Key": "Stack", + "Value": "editorial-feeds", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "VpcId": { + "Ref": "VpcId", + }, + }, + "Type": "AWS::EC2::SecurityGroup", + }, + "DefaultSecurityGroupNewswiresfromNewswiresDefaultSecurityGroupNewswires40BF4E635432BAB7D5C3": { + "Properties": { + "Description": "from NewswiresDefaultSecurityGroupNewswires40BF4E63:5432", + "FromPort": 5432, + "GroupId": { + "Fn::GetAtt": [ + "DefaultSecurityGroupNewswires91CCC0BF", + "GroupId", + ], + }, + "IpProtocol": "tcp", + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "DefaultSecurityGroupNewswires91CCC0BF", + "GroupId", + ], + }, + "ToPort": 5432, + }, + "Type": "AWS::EC2::SecurityGroupIngress", + }, + "DefaultSecurityGroupNewswiresfromNewswiresIngestionLambdaTESTSecurityGroup0E7FFFB65432CB5D2591": { + "Properties": { + "Description": "from NewswiresIngestionLambdaTESTSecurityGroup0E7FFFB6:5432", + "FromPort": 5432, + "GroupId": { + "Fn::GetAtt": [ + "DefaultSecurityGroupNewswires91CCC0BF", + "GroupId", + ], + }, + "IpProtocol": "tcp", + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "IngestionLambdaTESTSecurityGroup5E6CBDBD", + "GroupId", + ], + }, + "ToPort": 5432, + }, + "Type": "AWS::EC2::SecurityGroupIngress", + }, + "DefaultSecurityGroupNewswiresfromNewswiresLoadBalancerNewswiresSecurityGroup255ABDD1900011B4DAAD": { + "Properties": { + "Description": "Load balancer to target", + "FromPort": 9000, + "GroupId": { + "Fn::GetAtt": [ + "DefaultSecurityGroupNewswires91CCC0BF", + "GroupId", + ], + }, + "IpProtocol": "tcp", + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "LoadBalancerNewswiresSecurityGroupF93D2107", + "GroupId", + ], + }, + "ToPort": 9000, + }, + "Type": "AWS::EC2::SecurityGroupIngress", + }, + "DescribeEC2PolicyFF5F9295": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "autoscaling:DescribeAutoScalingInstances", + "autoscaling:DescribeAutoScalingGroups", + "ec2:DescribeTags", + "ec2:DescribeInstances", + ], + "Effect": "Allow", + "Resource": "*", + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "describe-ec2-policy", + "Roles": [ + { + "Ref": "InstanceRoleNewswiresC10A0615", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "DnsRecord": { + "Properties": { + "Name": "newswires.TEST.dev-gutools.co.uk", + "RecordType": "CNAME", + "ResourceRecords": [ + { + "Fn::GetAtt": [ + "LoadBalancerNewswires3B8BBCB3", + "DNSName", + ], + }, + ], + "Stage": "TEST", + "TTL": 60, + }, + "Type": "Guardian::DNS::RecordSet", + }, + "EXAMPLEfixedfrequencyLambdaD9A07041": { + "DependsOn": [ + "EXAMPLEfixedfrequencyLambdaServiceRoleDefaultPolicy272A9D10", + "EXAMPLEfixedfrequencyLambdaServiceRoleBA2F232F", + ], + "Properties": { + "Architectures": [ + "arm64", + ], + "Code": { + "S3Bucket": { + "Ref": "DistributionBucketName", + }, + "S3Key": "editorial-feeds/TEST/poller-lambdas/poller-lambdas.zip", + }, + "Environment": { + "Variables": { + "APP": "EXAMPLE_fixed_frequency_poller_lambda", + "INGESTION_LAMBDA_QUEUE_URL": { + "Fn::ImportValue": "mockWiresFeeds:ExportsOutputRefMockSourceQueue20B4D8C006FBA8DA", + }, + "OWN_QUEUE_URL": { + "Ref": "EXAMPLEfixedfrequencyLambdaQueueF55F5480", + }, + "STACK": "editorial-feeds", + "STAGE": "TEST", + }, + }, + "FunctionName": "editorial-feeds-TEST-EXAMPLE_fixed_frequency_poller_lambda", + "Handler": "index.EXAMPLE_fixed_frequency", + "LoggingConfig": { + "LogFormat": "JSON", + }, + "MemorySize": 128, + "RecursiveLoop": "Allow", + "ReservedConcurrentExecutions": 1, + "Role": { + "Fn::GetAtt": [ + "EXAMPLEfixedfrequencyLambdaServiceRoleBA2F232F", + "Arn", + ], + }, + "Runtime": "nodejs20.x", + "Tags": [ + { + "Key": "App", + "Value": "EXAMPLE_fixed_frequency_poller_lambda", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/newswires", + }, + { + "Key": "Stack", + "Value": "editorial-feeds", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "Timeout": 60, + }, + "Type": "AWS::Lambda::Function", + }, + "EXAMPLEfixedfrequencyLambdaQueueF55F5480": { + "DeletionPolicy": "Delete", + "Properties": { + "QueueName": "editorial-feeds-TEST-EXAMPLE_fixed_frequency_poller_lambda_queue", + "Tags": [ + { + "Key": "App", + "Value": "newswires", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/newswires", + }, + { + "Key": "Stack", + "Value": "editorial-feeds", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "VisibilityTimeout": 60, + }, + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + }, + "EXAMPLEfixedfrequencyLambdaServiceRoleBA2F232F": { + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ], + ], + }, + ], + "Tags": [ + { + "Key": "App", + "Value": "EXAMPLE_fixed_frequency_poller_lambda", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/newswires", + }, + { + "Key": "Stack", + "Value": "editorial-feeds", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + }, + "Type": "AWS::IAM::Role", + }, + "EXAMPLEfixedfrequencyLambdaServiceRoleDefaultPolicy272A9D10": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":s3:::", + { + "Ref": "DistributionBucketName", + }, + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":s3:::", + { + "Ref": "DistributionBucketName", + }, + "/editorial-feeds/TEST/poller-lambdas/poller-lambdas.zip", + ], + ], + }, + ], + }, + { + "Action": "ssm:GetParametersByPath", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":parameter/TEST/editorial-feeds/EXAMPLE_fixed_frequency_poller_lambda", + ], + ], + }, + }, + { + "Action": [ + "ssm:GetParameters", + "ssm:GetParameter", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":parameter/TEST/editorial-feeds/EXAMPLE_fixed_frequency_poller_lambda/*", + ], + ], + }, + }, + { + "Action": [ + "sqs:ReceiveMessage", + "sqs:ChangeMessageVisibility", + "sqs:GetQueueUrl", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EXAMPLEfixedfrequencyLambdaQueueF55F5480", + "Arn", + ], + }, + }, + { + "Action": [ + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl", + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EXAMPLEfixedfrequencyLambdaQueueF55F5480", + "Arn", + ], + }, + }, + { + "Action": [ + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl", + ], + "Effect": "Allow", + "Resource": { + "Fn::ImportValue": "mockWiresFeeds:ExportsOutputFnGetAttMockSourceQueue20B4D8C0ArnCE5A6D0C", + }, + }, + ], + "Version": "2012-10-17", + }, + "PolicyName": "EXAMPLEfixedfrequencyLambdaServiceRoleDefaultPolicy272A9D10", + "Roles": [ + { + "Ref": "EXAMPLEfixedfrequencyLambdaServiceRoleBA2F232F", + }, + ], + }, + "Type": "AWS::IAM::Policy", + }, + "EXAMPLEfixedfrequencyLambdaSqsEventSourceNewswiresEXAMPLEfixedfrequencyLambdaQueue19E585D8ADCAD8C9": { + "Properties": { + "BatchSize": 1, + "EventSourceArn": { + "Fn::GetAtt": [ + "EXAMPLEfixedfrequencyLambdaQueueF55F5480", + "Arn", + ], + }, + "FunctionName": { + "Ref": "EXAMPLEfixedfrequencyLambdaD9A07041", + }, + }, + "Type": "AWS::Lambda::EventSourceMapping", + }, + "EXAMPLElongpollingLambdaDC443D98": { + "DependsOn": [ + "EXAMPLElongpollingLambdaServiceRoleDefaultPolicy72E70E87", + "EXAMPLElongpollingLambdaServiceRoleF2C4DC08", + ], + "Properties": { + "Architectures": [ + "arm64", + ], + "Code": { + "S3Bucket": { + "Ref": "DistributionBucketName", + }, + "S3Key": "editorial-feeds/TEST/poller-lambdas/poller-lambdas.zip", + }, + "Environment": { + "Variables": { + "APP": "EXAMPLE_long_polling_poller_lambda", + "INGESTION_LAMBDA_QUEUE_URL": { + "Fn::ImportValue": "mockWiresFeeds:ExportsOutputRefMockSourceQueue20B4D8C006FBA8DA", + }, + "OWN_QUEUE_URL": { + "Ref": "EXAMPLElongpollingLambdaQueue0E2C8DED", + }, + "STACK": "editorial-feeds", + "STAGE": "TEST", + }, + }, + "FunctionName": "editorial-feeds-TEST-EXAMPLE_long_polling_poller_lambda", + "Handler": "index.EXAMPLE_long_polling", + "LoggingConfig": { + "LogFormat": "JSON", + }, + "MemorySize": 128, + "RecursiveLoop": "Allow", + "ReservedConcurrentExecutions": 1, + "Role": { + "Fn::GetAtt": [ + "EXAMPLElongpollingLambdaServiceRoleF2C4DC08", + "Arn", + ], + }, + "Runtime": "nodejs20.x", + "Tags": [ + { + "Key": "App", + "Value": "EXAMPLE_long_polling_poller_lambda", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/newswires", + }, + { + "Key": "Stack", + "Value": "editorial-feeds", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "Timeout": 60, + }, + "Type": "AWS::Lambda::Function", + }, + "EXAMPLElongpollingLambdaQueue0E2C8DED": { + "DeletionPolicy": "Delete", + "Properties": { + "QueueName": "editorial-feeds-TEST-EXAMPLE_long_polling_poller_lambda_queue", + "Tags": [ + { + "Key": "App", + "Value": "newswires", + }, + { + "Key": "gu:cdk:version", + "Value": "TEST", + }, + { + "Key": "gu:repo", + "Value": "guardian/newswires", + }, + { + "Key": "Stack", + "Value": "editorial-feeds", + }, + { + "Key": "Stage", + "Value": "TEST", + }, + ], + "VisibilityTimeout": 60, + }, + "Type": "AWS::SQS::Queue", + "UpdateReplacePolicy": "Delete", + }, + "EXAMPLElongpollingLambdaServiceRoleDefaultPolicy72E70E87": { + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject*", + "s3:GetBucket*", + "s3:List*", + ], + "Effect": "Allow", + "Resource": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":s3:::", + { + "Ref": "DistributionBucketName", + }, + ], + ], + }, + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":s3:::", + { + "Ref": "DistributionBucketName", + }, + "/editorial-feeds/TEST/poller-lambdas/poller-lambdas.zip", + ], + ], + }, + ], + }, + { + "Action": "ssm:GetParametersByPath", + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":parameter/TEST/editorial-feeds/EXAMPLE_long_polling_poller_lambda", + ], + ], + }, + }, + { + "Action": [ + "ssm:GetParameters", + "ssm:GetParameter", + ], + "Effect": "Allow", + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:", + { + "Ref": "AWS::Region", + }, + ":", + { + "Ref": "AWS::AccountId", + }, + ":parameter/TEST/editorial-feeds/EXAMPLE_long_polling_poller_lambda/*", + ], + ], + }, + }, + { + "Action": [ + "sqs:ReceiveMessage", + "sqs:ChangeMessageVisibility", + "sqs:GetQueueUrl", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EXAMPLElongpollingLambdaQueue0E2C8DED", + "Arn", + ], + }, + }, + { + "Action": [ + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl", + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "EXAMPLElongpollingLambdaQueue0E2C8DED", + "Arn", + ], + }, + }, + { + "Action": [ + "sqs:SendMessage", + "sqs:GetQueueAttributes", + "sqs:GetQueueUrl", + ], + "Effect": "Allow", + "Resource": { + "Fn::ImportValue": "mockWiresFeeds:ExportsOutputFnGetAttMockSourceQueue20B4D8C0ArnCE5A6D0C", + }, + }, + ], + "Version": "2012-10-17", }, - }, - "Type": "AWS::AutoScaling::AutoScalingGroup", - }, - "CertificateNewswiresFEA3DE56": { - "DeletionPolicy": "Retain", - "Properties": { - "DomainName": "newswires.TEST.dev-gutools.co.uk", - "Tags": [ - { - "Key": "App", - "Value": "newswires", - }, - { - "Key": "gu:cdk:version", - "Value": "TEST", - }, - { - "Key": "gu:repo", - "Value": "guardian/newswires", - }, - { - "Key": "Name", - "Value": "Newswires/CertificateNewswires", - }, - { - "Key": "Stack", - "Value": "editorial-feeds", - }, + "PolicyName": "EXAMPLElongpollingLambdaServiceRoleDefaultPolicy72E70E87", + "Roles": [ { - "Key": "Stage", - "Value": "TEST", + "Ref": "EXAMPLElongpollingLambdaServiceRoleF2C4DC08", }, ], - "ValidationMethod": "DNS", }, - "Type": "AWS::CertificateManager::Certificate", - "UpdateReplacePolicy": "Retain", + "Type": "AWS::IAM::Policy", }, - "DefaultSecurityGroupNewswires91CCC0BF": { + "EXAMPLElongpollingLambdaServiceRoleF2C4DC08": { "Properties": { - "GroupDescription": "Newswires/DefaultSecurityGroupNewswires", - "SecurityGroupEgress": [ + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com", + }, + }, + ], + "Version": "2012-10-17", + }, + "ManagedPolicyArns": [ { - "CidrIp": "0.0.0.0/0", - "Description": "Allow all outbound traffic by default", - "IpProtocol": "-1", + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition", + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", + ], + ], }, ], "Tags": [ { "Key": "App", - "Value": "newswires", + "Value": "EXAMPLE_long_polling_poller_lambda", }, { "Key": "gu:cdk:version", @@ -234,117 +948,23 @@ exports[`The Newswires stack matches the snapshot 1`] = ` "Value": "TEST", }, ], - "VpcId": { - "Ref": "VpcId", - }, - }, - "Type": "AWS::EC2::SecurityGroup", - }, - "DefaultSecurityGroupNewswiresfromNewswiresDefaultSecurityGroupNewswires40BF4E635432BAB7D5C3": { - "Properties": { - "Description": "from NewswiresDefaultSecurityGroupNewswires40BF4E63:5432", - "FromPort": 5432, - "GroupId": { - "Fn::GetAtt": [ - "DefaultSecurityGroupNewswires91CCC0BF", - "GroupId", - ], - }, - "IpProtocol": "tcp", - "SourceSecurityGroupId": { - "Fn::GetAtt": [ - "DefaultSecurityGroupNewswires91CCC0BF", - "GroupId", - ], - }, - "ToPort": 5432, - }, - "Type": "AWS::EC2::SecurityGroupIngress", - }, - "DefaultSecurityGroupNewswiresfromNewswiresIngestionLambdaTESTSecurityGroup0E7FFFB65432CB5D2591": { - "Properties": { - "Description": "from NewswiresIngestionLambdaTESTSecurityGroup0E7FFFB6:5432", - "FromPort": 5432, - "GroupId": { - "Fn::GetAtt": [ - "DefaultSecurityGroupNewswires91CCC0BF", - "GroupId", - ], - }, - "IpProtocol": "tcp", - "SourceSecurityGroupId": { - "Fn::GetAtt": [ - "IngestionLambdaTESTSecurityGroup5E6CBDBD", - "GroupId", - ], - }, - "ToPort": 5432, }, - "Type": "AWS::EC2::SecurityGroupIngress", + "Type": "AWS::IAM::Role", }, - "DefaultSecurityGroupNewswiresfromNewswiresLoadBalancerNewswiresSecurityGroup255ABDD1900011B4DAAD": { + "EXAMPLElongpollingLambdaSqsEventSourceNewswiresEXAMPLElongpollingLambdaQueue7E62ACD815B2FCD9": { "Properties": { - "Description": "Load balancer to target", - "FromPort": 9000, - "GroupId": { - "Fn::GetAtt": [ - "DefaultSecurityGroupNewswires91CCC0BF", - "GroupId", - ], - }, - "IpProtocol": "tcp", - "SourceSecurityGroupId": { + "BatchSize": 1, + "EventSourceArn": { "Fn::GetAtt": [ - "LoadBalancerNewswiresSecurityGroupF93D2107", - "GroupId", + "EXAMPLElongpollingLambdaQueue0E2C8DED", + "Arn", ], }, - "ToPort": 9000, - }, - "Type": "AWS::EC2::SecurityGroupIngress", - }, - "DescribeEC2PolicyFF5F9295": { - "Properties": { - "PolicyDocument": { - "Statement": [ - { - "Action": [ - "autoscaling:DescribeAutoScalingInstances", - "autoscaling:DescribeAutoScalingGroups", - "ec2:DescribeTags", - "ec2:DescribeInstances", - ], - "Effect": "Allow", - "Resource": "*", - }, - ], - "Version": "2012-10-17", + "FunctionName": { + "Ref": "EXAMPLElongpollingLambdaDC443D98", }, - "PolicyName": "describe-ec2-policy", - "Roles": [ - { - "Ref": "InstanceRoleNewswiresC10A0615", - }, - ], - }, - "Type": "AWS::IAM::Policy", - }, - "DnsRecord": { - "Properties": { - "Name": "newswires.TEST.dev-gutools.co.uk", - "RecordType": "CNAME", - "ResourceRecords": [ - { - "Fn::GetAtt": [ - "LoadBalancerNewswires3B8BBCB3", - "DNSName", - ], - }, - ], - "Stage": "TEST", - "TTL": 60, }, - "Type": "Guardian::DNS::RecordSet", + "Type": "AWS::Lambda::EventSourceMapping", }, "GetDistributablePolicyNewswires3D555C70": { "Properties": { @@ -621,6 +1241,9 @@ exports[`The Newswires stack matches the snapshot 1`] = ` "IngestionLambdaTESTServiceRole62DE083D", ], "Properties": { + "Architectures": [ + "arm64", + ], "Code": { "S3Bucket": { "Ref": "DistributionBucketName", @@ -903,7 +1526,20 @@ exports[`The Newswires stack matches the snapshot 1`] = ` ], "Effect": "Allow", "Resource": { - "Fn::ImportValue": "mockWiresFeeds:ExportsOutputFnGetAttmockqueueCFAAE0C0Arn5189C54F", + "Fn::ImportValue": "mockWiresFeeds:ExportsOutputFnGetAttMockSourceQueue20B4D8C0ArnCE5A6D0C", + }, + }, + { + "Action": [ + "sqs:ReceiveMessage", + "sqs:ChangeMessageVisibility", + "sqs:GetQueueUrl", + "sqs:DeleteMessage", + "sqs:GetQueueAttributes", + ], + "Effect": "Allow", + "Resource": { + "Fn::ImportValue": "mockWiresFeeds:ExportsOutputFnGetAttMockFingerpostQueue05E3D7ECArnA85146B3", }, }, { @@ -987,10 +1623,24 @@ exports[`The Newswires stack matches the snapshot 1`] = ` }, "Type": "AWS::IAM::Policy", }, - "IngestionLambdaTESTSqsEventSourcemockWiresFeedsmockqueue7ED5F3492F0994D1": { + "IngestionLambdaTESTSqsEventSourcemockWiresFeedsMockFingerpostQueue19AE08DA454EF2E1": { + "Properties": { + "EventSourceArn": { + "Fn::ImportValue": "mockWiresFeeds:ExportsOutputFnGetAttMockFingerpostQueue05E3D7ECArnA85146B3", + }, + "FunctionName": { + "Ref": "IngestionLambdaTESTC02B3E43", + }, + "FunctionResponseTypes": [ + "ReportBatchItemFailures", + ], + }, + "Type": "AWS::Lambda::EventSourceMapping", + }, + "IngestionLambdaTESTSqsEventSourcemockWiresFeedsMockSourceQueue17F9CFF698B624D6": { "Properties": { "EventSourceArn": { - "Fn::ImportValue": "mockWiresFeeds:ExportsOutputFnGetAttmockqueueCFAAE0C0Arn5189C54F", + "Fn::ImportValue": "mockWiresFeeds:ExportsOutputFnGetAttMockSourceQueue20B4D8C0ArnCE5A6D0C", }, "FunctionName": { "Ref": "IngestionLambdaTESTC02B3E43", diff --git a/cdk/lib/constants.ts b/cdk/lib/constants.ts new file mode 100644 index 00000000..c2ad59d3 --- /dev/null +++ b/cdk/lib/constants.ts @@ -0,0 +1,4 @@ +import * as lambda from 'aws-cdk-lib/aws-lambda'; + +export const LAMBDA_RUNTIME = lambda.Runtime.NODEJS_20_X; +export const LAMBDA_ARCHITECTURE = lambda.Architecture.ARM_64; diff --git a/cdk/lib/constructs/pollerLambda.ts b/cdk/lib/constructs/pollerLambda.ts new file mode 100644 index 00000000..70ea0c7c --- /dev/null +++ b/cdk/lib/constructs/pollerLambda.ts @@ -0,0 +1,72 @@ +import { aws_sqs, Duration } from 'aws-cdk-lib'; +import { GuLambdaFunction } from '@guardian/cdk/lib/constructs/lambda'; +import { GuStack } from '@guardian/cdk/lib/constructs/core'; +import { LAMBDA_ARCHITECTURE, LAMBDA_RUNTIME } from '../constants'; +import { SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; +import { + POLLER_LAMBDA_ENV_VAR_KEYS, + PollerConfig, +} from '../../../shared/pollers'; +import { RecursiveLoop } from 'aws-cdk-lib/aws-lambda'; + +interface PollerLambdaProps { + pollerId: string; + pollerConfig: PollerConfig; + ingestionLambdaQueue: aws_sqs.Queue; +} + +export class PollerLambda /*extends GuAppAwareConstruct*/ { + constructor( + scope: GuStack, + { pollerId, ingestionLambdaQueue, pollerConfig }: PollerLambdaProps, + ) { + // super(scope, id); + + // TODO secrets manager secret + + const timeout = Duration.seconds( + pollerConfig.overrideLambdaTimeoutSeconds || 60, // TODO consider also taking into account the 'idealFrequencyInSeconds' if specified + ); + + const lambdaAppName = `${pollerId}_poller_lambda`; + + // we use queue here to allow lambda to call itself, but sometimes with a delay + const lambdaQueue = new aws_sqs.Queue(scope, `${pollerId}LambdaQueue`, { + queueName: `${scope.stack}-${scope.stage}-${lambdaAppName}_queue`, + visibilityTimeout: timeout, // must be at least the same as the lambda + }); + + const lambda = new GuLambdaFunction(scope, `${pollerId}Lambda`, { + app: lambdaAppName, // varying app tag for each lambda allows riff-raff to find by tag + functionName: `${scope.stack}-${scope.stage}-${lambdaAppName}`, + runtime: LAMBDA_RUNTIME, + architecture: LAMBDA_ARCHITECTURE, + recursiveLoop: RecursiveLoop.ALLOW, // this allows the lambda to indirectly call itself via the SQS queue + reservedConcurrentExecutions: 1, + environment: { + [POLLER_LAMBDA_ENV_VAR_KEYS.INGESTION_LAMBDA_QUEUE_URL]: + ingestionLambdaQueue.queueUrl, + [POLLER_LAMBDA_ENV_VAR_KEYS.OWN_QUEUE_URL]: lambdaQueue.queueUrl, + // TODO pass the name of the secret in as environment variable + }, + memorySize: pollerConfig.overrideLambdaMemoryMB || 128, + timeout, + handler: `index.${pollerId}`, // see programmatically generated exports in poller-lambdas/src/index.ts + withoutFilePrefix: true, // we are using a single zip file for all poller lambdas, so we use stack and stage below, but avoids app being part of the bucket key + fileName: `${scope.stack}/${scope.stage}/poller-lambdas/poller-lambdas.zip`, // single zip file containing all poller lambdas + }); + + // TODO grant lambda permission to read secret (at runtime) + + // wire up lambda to process its own queue + lambda.addEventSource(new SqsEventSource(lambdaQueue, { batchSize: 1 })); + + // give permission to lambda to write back to its own queue (to facilitate recursive calling) + lambdaQueue.grantSendMessages(lambda); + + // allow lambda to write to the ingestion-lambdas queue + ingestionLambdaQueue.grantSendMessages(lambda); //TODO consider making that queue a destination for the poller-lambda and then the lambda just returns the payload (on success and failure) rather than using SQS SDK within the lambda + + // TODO alarms for too frequent and stalled + } +} diff --git a/cdk/lib/newswires.test.ts b/cdk/lib/newswires.test.ts index eab8edfb..98bdf715 100644 --- a/cdk/lib/newswires.test.ts +++ b/cdk/lib/newswires.test.ts @@ -5,10 +5,12 @@ import { Queue } from 'aws-cdk-lib/aws-sqs'; import { GuStack, GuStackProps } from '@guardian/cdk/lib/constructs/core'; class MockWiresFeedsStack extends GuStack { + public readonly mockSourceQueue: Queue; public readonly mockFingerpostQueue: Queue; constructor(scope: App, id: string, props: GuStackProps) { super(scope, id, props); - this.mockFingerpostQueue = new Queue(this, 'mock-queue'); + this.mockSourceQueue = new Queue(this, 'MockSourceQueue'); + this.mockFingerpostQueue = new Queue(this, 'MockFingerpostQueue'); } } @@ -16,7 +18,7 @@ describe('The Newswires stack', () => { it('matches the snapshot', () => { const app = new App(); - const { mockFingerpostQueue } = new MockWiresFeedsStack( + const { mockFingerpostQueue, mockSourceQueue } = new MockWiresFeedsStack( app, 'mockWiresFeeds', { stack: 'editorial-feeds', stage: 'TEST' }, @@ -28,6 +30,7 @@ describe('The Newswires stack', () => { domainName: 'newswires.TEST.dev-gutools.co.uk', enableMonitoring: true, fingerpostQueue: mockFingerpostQueue, + sourceQueue: mockSourceQueue, }); const template = Template.fromStack(stack); expect(template.toJSON()).toMatchSnapshot(); diff --git a/cdk/lib/newswires.ts b/cdk/lib/newswires.ts index 04089a7c..dee32dc8 100644 --- a/cdk/lib/newswires.ts +++ b/cdk/lib/newswires.ts @@ -17,7 +17,6 @@ import { InstanceType, Port, } from 'aws-cdk-lib/aws-ec2'; -import { Runtime } from 'aws-cdk-lib/aws-lambda'; import { SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; import { DatabaseInstanceEngine, @@ -27,8 +26,12 @@ import { import { Topic } from 'aws-cdk-lib/aws-sns'; import type { Queue } from 'aws-cdk-lib/aws-sqs'; import { GuDatabase } from './constructs/database'; +import { LAMBDA_ARCHITECTURE, LAMBDA_RUNTIME } from './constants'; +import { POLLERS_CONFIG } from '../../shared/pollers'; +import { PollerLambda } from './constructs/pollerLambda'; export type NewswiresProps = GuStackProps & { + sourceQueue: Queue; fingerpostQueue: Queue; domainName: string; enableMonitoring: boolean; @@ -83,7 +86,8 @@ export class Newswires extends GuStack { `IngestionLambda-${this.stage}`, { app: 'ingestion-lambda', - runtime: Runtime.NODEJS_20_X, + runtime: LAMBDA_RUNTIME, + architecture: LAMBDA_ARCHITECTURE, handler: 'handler.main', // each execution can handle up to 10 messages, so this is up // to 100 messages in flight; should be more than powerful enough @@ -105,7 +109,7 @@ export class Newswires extends GuStack { ingestionLambda.connections.allowTo(database, Port.tcp(5432)); - const eventSource = new SqsEventSource(props.fingerpostQueue, { + const eventSourceProps = { /** * This is required to allow us to deal with failures of particular * records handed to a lambda by an SQS queue. Without this, the @@ -116,9 +120,13 @@ export class Newswires extends GuStack { * */ reportBatchItemFailures: true, - }); - - ingestionLambda.addEventSource(eventSource); + }; + ingestionLambda.addEventSource( + new SqsEventSource(props.sourceQueue, eventSourceProps), + ); + ingestionLambda.addEventSource( + new SqsEventSource(props.fingerpostQueue, eventSourceProps), + ); feedsBucket.grantWrite(ingestionLambda); @@ -160,6 +168,15 @@ export class Newswires extends GuStack { } : { noMonitoring: true }; + Object.entries(POLLERS_CONFIG).map( + ([pollerId, pollerConfig]) => + new PollerLambda(this, { + pollerId, + pollerConfig, + ingestionLambdaQueue: props.sourceQueue, + }), + ); + const newswiresApp = new GuPlayApp(this, { app, access: { scope: AccessScope.PUBLIC }, diff --git a/cdk/lib/wires-feeds.ts b/cdk/lib/wires-feeds.ts index f9014e0d..9c0b41d0 100644 --- a/cdk/lib/wires-feeds.ts +++ b/cdk/lib/wires-feeds.ts @@ -11,6 +11,7 @@ export type WiresFeedsProps = GuStackProps & {}; const app = 'wires-feeds'; export class WiresFeeds extends GuStack { + public readonly sourceQueue: Queue; public readonly fingerpostQueue: Queue; constructor(scope: App, id: string, props: WiresFeedsProps) { @@ -70,10 +71,8 @@ export class WiresFeeds extends GuStack { return queue; } - /** A topic and queue for the 'raw' wires feed. - * Not receiving data yet so we aren't currently doing anything more with it. - */ - createTopicQueue(this, 'source'); + /** A topic and queue for the 'raw' wires feed.*/ + this.sourceQueue = createTopicQueue(this, 'source'); this.fingerpostQueue = createTopicQueue(this, 'fingerpost'); } diff --git a/package-lock.json b/package-lock.json index 25c91043..4bf3ad5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "workspaces": [ "cdk", - "ingestion-lambda" + "ingestion-lambda", + "poller-lambdas" ], "devDependencies": { "@guardian/eslint-config-typescript": "8.0.0", @@ -907,6 +908,538 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/client-sqs": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.682.0.tgz", + "integrity": "sha512-93r0i2VwiHiZkcXfWVoxMpyw91Ou0C6gyS7AzPHoZ9ZoXV1VaBFqQ/FmcLzzNa9pwjE6k/Pn7VJMNKBezE8EmQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/client-sts": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-sdk-sqs": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/md5-js": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/client-sso": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.682.0.tgz", + "integrity": "sha512-PYH9RFUMYLFl66HSBq4tIx6fHViMLkhJHTYJoJONpBs+Td+NwVJ895AdLtDsBIhMS0YseCbPpuyjUCJgsUrwUw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.682.0.tgz", + "integrity": "sha512-ZPZ7Y/r/w3nx/xpPzGSqSQsB090Xk5aZZOH+WBhTDn/pBEuim09BYXCLzvvxb7R7NnuoQdrTJiwimdJAhHl7ZQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/client-sts": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.682.0.tgz", + "integrity": "sha512-xKuo4HksZ+F8m9DOfx/ZuWNhaPuqZFPwwy0xqcBT6sWH7OAuBjv/fnpOTzyQhpVTWddlf+ECtMAMrxjxuOExGQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-node": "3.682.0", + "@aws-sdk/middleware-host-header": "3.679.0", + "@aws-sdk/middleware-logger": "3.679.0", + "@aws-sdk/middleware-recursion-detection": "3.679.0", + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/region-config-resolver": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@aws-sdk/util-user-agent-browser": "3.679.0", + "@aws-sdk/util-user-agent-node": "3.682.0", + "@smithy/config-resolver": "^3.0.9", + "@smithy/core": "^2.4.8", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/hash-node": "^3.0.7", + "@smithy/invalid-dependency": "^3.0.7", + "@smithy/middleware-content-length": "^3.0.9", + "@smithy/middleware-endpoint": "^3.1.4", + "@smithy/middleware-retry": "^3.0.23", + "@smithy/middleware-serde": "^3.0.7", + "@smithy/middleware-stack": "^3.0.7", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/url-parser": "^3.0.7", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.23", + "@smithy/util-defaults-mode-node": "^3.0.23", + "@smithy/util-endpoints": "^2.1.3", + "@smithy/util-middleware": "^3.0.7", + "@smithy/util-retry": "^3.0.7", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/core": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.679.0.tgz", + "integrity": "sha512-CS6PWGX8l4v/xyvX8RtXnBisdCa5+URzKd0L6GvHChype9qKUVxO/Gg6N/y43Hvg7MNWJt9FBPNWIxUB+byJwg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/signature-v4": "^4.2.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-middleware": "^3.0.7", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-env": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.679.0.tgz", + "integrity": "sha512-EdlTYbzMm3G7VUNAMxr9S1nC1qUNqhKlAxFU8E7cKsAe8Bp29CD5HAs3POc56AVo9GC4yRIS+/mtlZSmrckzUA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-http": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.679.0.tgz", + "integrity": "sha512-ZoKLubW5DqqV1/2a3TSn+9sSKg0T8SsYMt1JeirnuLJF0mCoYFUaWMyvxxKuxPoqvUsaycxKru4GkpJ10ltNBw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/fetch-http-handler": "^3.2.9", + "@smithy/node-http-handler": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/protocol-http": "^4.1.4", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-stream": "^3.1.9", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.682.0.tgz", + "integrity": "sha512-6eqWeHdK6EegAxqDdiCi215nT3QZPwukgWAYuVxNfJ/5m0/P7fAzF+D5kKVgByUvGJEbq/FEL8Fw7OBe64AA+g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.682.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-node": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.682.0.tgz", + "integrity": "sha512-HSmDqZcBVZrTctHCT9m++vdlDfJ1ARI218qmZa+TZzzOFNpKWy6QyHMEra45GB9GnkkMmV6unoDSPMuN0AqcMg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.679.0", + "@aws-sdk/credential-provider-http": "3.679.0", + "@aws-sdk/credential-provider-ini": "3.682.0", + "@aws-sdk/credential-provider-process": "3.679.0", + "@aws-sdk/credential-provider-sso": "3.682.0", + "@aws-sdk/credential-provider-web-identity": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/credential-provider-imds": "^3.2.4", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-process": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.679.0.tgz", + "integrity": "sha512-u/p4TV8kQ0zJWDdZD4+vdQFTMhkDEJFws040Gm113VHa/Xo1SYOjbpvqeuFoz6VmM0bLvoOWjxB9MxnSQbwKpQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.682.0.tgz", + "integrity": "sha512-h7IH1VsWgV6YAJSWWV6y8uaRjGqLY3iBpGZlXuTH/c236NMLaNv+WqCBLeBxkFGUb2WeQ+FUPEJDCD69rgLIkg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.682.0", + "@aws-sdk/core": "3.679.0", + "@aws-sdk/token-providers": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.679.0.tgz", + "integrity": "sha512-a74tLccVznXCaBefWPSysUcLXYJiSkeUmQGtalNgJ1vGkE36W5l/8czFiiowdWdKWz7+x6xf0w+Kjkjlj42Ung==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-host-header": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.679.0.tgz", + "integrity": "sha512-y176HuQ8JRY3hGX8rQzHDSbCl9P5Ny9l16z4xmaiLo+Qfte7ee4Yr3yaAKd7GFoJ3/Mhud2XZ37fR015MfYl2w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-logger": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.679.0.tgz", + "integrity": "sha512-0vet8InEj7nvIvGKk+ch7bEF5SyZ7Us9U7YTEgXPrBNStKeRUsgwRm0ijPWWd0a3oz2okaEwXsFl7G/vI0XiEA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.679.0.tgz", + "integrity": "sha512-sQoAZFsQiW/LL3DfKMYwBoGjYDEnMbA9WslWN8xneCmBAwKo6IcSksvYs23PP8XMIoBGe2I2J9BSr654XWygTQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.682.0.tgz", + "integrity": "sha512-7TyvYR9HdGH1/Nq0eeApUTM4izB6rExiw87khVYuJwZHr6FmvIL1FsOVFro/4WlXa0lg4LiYOm/8H8dHv+fXTg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "3.679.0", + "@aws-sdk/types": "3.679.0", + "@aws-sdk/util-endpoints": "3.679.0", + "@smithy/core": "^2.4.8", + "@smithy/protocol-http": "^4.1.4", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/region-config-resolver": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.679.0.tgz", + "integrity": "sha512-Ybx54P8Tg6KKq5ck7uwdjiKif7n/8g1x+V0V9uTjBjRWqaIgiqzXwKWoPj6NCNkE7tJNtqI4JrNxp/3S3HvmRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.7", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/token-providers": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.679.0.tgz", + "integrity": "sha512-1/+Zso/x2jqgutKixYFQEGli0FELTgah6bm7aB+m2FAWH4Hz7+iMUsazg6nSWm714sG9G3h5u42Dmpvi9X6/hA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/property-provider": "^3.1.7", + "@smithy/shared-ini-file-loader": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.679.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/types": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.679.0.tgz", + "integrity": "sha512-NwVq8YvInxQdJ47+zz4fH3BRRLC6lL+WLkvr242PVBbUOLRyK/lkwHlfiKUoeVIMyK5NF+up6TRg71t/8Bny6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-endpoints": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.679.0.tgz", + "integrity": "sha512-YL6s4Y/1zC45OvddvgE139fjeWSKKPgLlnfrvhVL7alNyY9n7beR4uhoDpNrt5mI6sn9qiBF17790o+xLAXjjg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "@smithy/util-endpoints": "^2.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.679.0.tgz", + "integrity": "sha512-CusSm2bTBG1kFypcsqU8COhnYc6zltobsqs3nRrvYqYaOqtMnuE46K4XTWpnzKgwDejgZGOE+WYyprtAxrPvmQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/types": "^3.5.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/client-sqs/node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.682.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.682.0.tgz", + "integrity": "sha512-so5s+j0gPoTS0HM4HPL+G0ajk0T6cQAg8JXzRgvyiQAxqie+zGCZAV3VuVeMNWMVbzsgZl0pYZaatPFTLG/AxA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/middleware-user-agent": "3.682.0", + "@aws-sdk/types": "3.679.0", + "@smithy/node-config-provider": "^3.1.8", + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, "node_modules/@aws-sdk/client-sso": { "version": "3.637.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.637.0.tgz", @@ -1394,6 +1927,36 @@ "node": ">=16.0.0" } }, + "node_modules/@aws-sdk/middleware-sdk-sqs": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.679.0.tgz", + "integrity": "sha512-GjOpT9GRMH6n3Rm9ZsRsrIbLxBPE3/L1KMkIn2uZj14uqz1pdE4ALCN9b9ZkPN+L//rsUrYqtd9gq9Hn9c2FJw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.679.0", + "@smithy/smithy-client": "^3.4.0", + "@smithy/types": "^3.5.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-sqs/node_modules/@aws-sdk/types": { + "version": "3.679.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.679.0.tgz", + "integrity": "sha512-NwVq8YvInxQdJ47+zz4fH3BRRLC6lL+WLkvr242PVBbUOLRyK/lkwHlfiKUoeVIMyK5NF+up6TRg71t/8Bny6Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.5.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@aws-sdk/middleware-ssec": { "version": "3.609.0", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.609.0.tgz", @@ -3336,12 +3899,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", - "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.6.tgz", + "integrity": "sha512-0XuhuHQlEqbNQZp7QxxrFTdVWdwxch4vjxYgfInF91hZFkPxf9QDrdQka0KfxFMPqLNzSw0b95uGTrLliQUavQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3368,15 +3931,15 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", - "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.10.tgz", + "integrity": "sha512-Uh0Sz9gdUuz538nvkPiyv1DZRX9+D15EKDtnQP5rYVAzM/dnYk3P8cg73jcxyOitPgT3mE3OVj7ky7sibzHWkw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/types": "^3.6.0", "@smithy/util-config-provider": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.8", "tslib": "^2.6.2" }, "engines": { @@ -3384,19 +3947,17 @@ } }, "node_modules/@smithy/core": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz", - "integrity": "sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.5.1.tgz", + "integrity": "sha512-DujtuDA7BGEKExJ05W5OdxCoyekcKT3Rhg1ZGeiUWaz2BJIWXjZmsG/DIP4W48GHno7AQwRsaCb8NcBgH3QZpg==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-retry": "^3.0.15", - "@smithy/middleware-serde": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/middleware-serde": "^3.0.8", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", "@smithy/util-body-length-browser": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-stream": "^3.2.1", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" }, @@ -3405,15 +3966,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", - "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.5.tgz", + "integrity": "sha512-4FTQGAsuwqTzVMmiRVTn0RR9GrbRfkP0wfu/tXWVHd2LgNpTY0uglQpIScXK4NaEyXbB3JmZt8gfVqO50lP8wg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/property-provider": "^3.1.8", + "@smithy/types": "^3.6.0", + "@smithy/url-parser": "^3.0.8", "tslib": "^2.6.2" }, "engines": { @@ -3488,14 +4049,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", - "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.9.tgz", + "integrity": "sha512-hYNVQOqhFQ6vOpenifFME546f0GfJn2OiQ3M0FDmuUu8V/Uiwy2wej7ZXxFBNqdx0R5DZAqWM1l6VRhGz8oE6A==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.4", + "@smithy/querystring-builder": "^3.0.7", + "@smithy/types": "^3.5.0", "@smithy/util-base64": "^3.0.0", "tslib": "^2.6.2" } @@ -3513,12 +4074,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", - "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.8.tgz", + "integrity": "sha512-tlNQYbfpWXHimHqrvgo14DrMAgUBua/cNoz9fMYcDmYej7MAmUcjav/QKQbFc3NrcPxeJ7QClER4tWZmfwoPng==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -3542,12 +4103,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", - "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.8.tgz", + "integrity": "sha512-7Qynk6NWtTQhnGTTZwks++nJhQ1O54Mzi7fz4PqZOiYXb4Z1Flpb2yRvdALoggTS8xjtohWUM+RygOtB30YL3Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" } }, @@ -3564,24 +4125,24 @@ } }, "node_modules/@smithy/md5-js": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.3.tgz", - "integrity": "sha512-O/SAkGVwpWmelpj/8yDtsaVe6sINHLB1q8YE/+ZQbDxIw3SRLbTZuRaI10K12sVoENdnHqzPp5i3/H+BcZ3m3Q==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-3.0.8.tgz", + "integrity": "sha512-LwApfTK0OJ/tCyNUXqnWCKoE2b4rDSr4BJlDAVCkiWYeHESr+y+d5zlAanuLW6fnitVJRD/7d9/kN/ZM9Su4mA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" } }, "node_modules/@smithy/middleware-content-length": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", - "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.10.tgz", + "integrity": "sha512-T4dIdCs1d/+/qMpwhJ1DzOhxCZjZHbHazEPJWdB4GDi2HjIZllVzeBEcdJUN0fomV8DURsgOyrbEUzg3vzTaOg==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3589,17 +4150,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", - "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.1.tgz", + "integrity": "sha512-wWO3xYmFm6WRW8VsEJ5oU6h7aosFXfszlz3Dj176pTij6o21oZnzkCLzShfmRaaCHDkBXWBdO0c4sQAvLFP6zA==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^3.0.3", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", - "@smithy/url-parser": "^3.0.3", - "@smithy/util-middleware": "^3.0.3", + "@smithy/core": "^2.5.1", + "@smithy/middleware-serde": "^3.0.8", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/shared-ini-file-loader": "^3.1.9", + "@smithy/types": "^3.6.0", + "@smithy/url-parser": "^3.0.8", + "@smithy/util-middleware": "^3.0.8", "tslib": "^2.6.2" }, "engines": { @@ -3607,18 +4169,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz", - "integrity": "sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==", + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.25.tgz", + "integrity": "sha512-m1F70cPaMBML4HiTgCw5I+jFNtjgz5z5UdGnUbG37vw6kh4UvizFYjqJGHvicfgKMkDL6mXwyPp5mhZg02g5sg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/protocol-http": "^4.1.0", - "@smithy/service-error-classification": "^3.0.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", - "@smithy/util-middleware": "^3.0.3", - "@smithy/util-retry": "^3.0.3", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/protocol-http": "^4.1.5", + "@smithy/service-error-classification": "^3.0.8", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", + "@smithy/util-middleware": "^3.0.8", + "@smithy/util-retry": "^3.0.8", "tslib": "^2.6.2", "uuid": "^9.0.1" }, @@ -3627,12 +4189,12 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", - "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.8.tgz", + "integrity": "sha512-Xg2jK9Wc/1g/MBMP/EUn2DLspN8LNt+GMe7cgF+Ty3vl+Zvu+VeZU5nmhveU+H8pxyTsjrAkci8NqY6OuvZnjA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3640,12 +4202,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", - "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.8.tgz", + "integrity": "sha512-d7ZuwvYgp1+3682Nx0MD3D/HtkmZd49N3JUndYWQXfRZrYEnCWYc8BHcNmVsPAp9gKvlurdg/mubE6b/rPS9MA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3653,14 +4215,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", - "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.9.tgz", + "integrity": "sha512-qRHoah49QJ71eemjuS/WhUXB+mpNtwHRWQr77J/m40ewBVVwvo52kYAmb7iuaECgGTTcYxHS4Wmewfwy++ueew==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/shared-ini-file-loader": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/property-provider": "^3.1.8", + "@smithy/shared-ini-file-loader": "^3.1.9", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3668,15 +4230,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", - "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.2.5.tgz", + "integrity": "sha512-PkOwPNeKdvX/jCpn0A8n9/TyoxjGZB8WVoJmm9YzsnAgggTj4CrjpRHlTQw7dlLZ320n1mY1y+nTRUDViKi/3w==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^3.1.1", - "@smithy/protocol-http": "^4.1.0", - "@smithy/querystring-builder": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/abort-controller": "^3.1.6", + "@smithy/protocol-http": "^4.1.5", + "@smithy/querystring-builder": "^3.0.8", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3684,12 +4246,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", - "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.8.tgz", + "integrity": "sha512-ukNUyo6rHmusG64lmkjFeXemwYuKge1BJ8CtpVKmrxQxc6rhUX0vebcptFA9MmrGsnLhwnnqeH83VTU9hwOpjA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3697,12 +4259,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", - "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.5.tgz", + "integrity": "sha512-hsjtwpIemmCkm3ZV5fd/T0bPIugW1gJXwZ/hpuVubt2hEUApIoUTrf6qIdh9MAWlw0vjMrA1ztJLAwtNaZogvg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3710,12 +4272,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", - "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.8.tgz", + "integrity": "sha512-btYxGVqFUARbUrN6VhL9c3dnSviIwBYD9Rz1jHuN1hgh28Fpv2xjU1HeCeDJX68xctz7r4l1PBnFhGg1WBBPuA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "@smithy/util-uri-escape": "^3.0.0", "tslib": "^2.6.2" }, @@ -3724,12 +4286,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", - "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.8.tgz", + "integrity": "sha512-BtEk3FG7Ks64GAbt+JnKqwuobJNX8VmFLBsKIwWr1D60T426fGrV2L3YS5siOcUhhp6/Y6yhBw1PSPxA5p7qGg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3737,24 +4299,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", - "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.8.tgz", + "integrity": "sha512-uEC/kCCFto83bz5ZzapcrgGqHOh/0r69sZ2ZuHlgoD5kYgXJEThCoTuw/y1Ub3cE7aaKdznb+jD9xRPIfIwD7g==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0" + "@smithy/types": "^3.6.0" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", - "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.9.tgz", + "integrity": "sha512-/+OsJRNtoRbtsX0UpSgWVxFZLsJHo/4sTr+kBg/J78sr7iC+tHeOvOJrS5hCpVQ6sWBbhWLp1UNiuMyZhE6pmA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3762,16 +4324,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", - "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.2.1.tgz", + "integrity": "sha512-NsV1jF4EvmO5wqmaSzlnTVetemBS3FZHdyc5CExbDljcyJCEEkJr8ANu2JvtNbVg/9MvKAWV44kTrGS+Pi4INg==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^3.0.0", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", "@smithy/util-hex-encoding": "^3.0.0", - "@smithy/util-middleware": "^3.0.3", + "@smithy/util-middleware": "^3.0.8", "@smithy/util-uri-escape": "^3.0.0", "@smithy/util-utf8": "^3.0.0", "tslib": "^2.6.2" @@ -3781,16 +4343,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz", - "integrity": "sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.4.2.tgz", + "integrity": "sha512-dxw1BDxJiY9/zI3cBqfVrInij6ShjpV4fmGHesGZZUiP9OSE/EVfdwdRz0PgvkEvrZHpsj2htRaHJfftE8giBA==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-endpoint": "^3.1.0", - "@smithy/middleware-stack": "^3.0.3", - "@smithy/protocol-http": "^4.1.0", - "@smithy/types": "^3.3.0", - "@smithy/util-stream": "^3.1.3", + "@smithy/core": "^2.5.1", + "@smithy/middleware-endpoint": "^3.2.1", + "@smithy/middleware-stack": "^3.0.8", + "@smithy/protocol-http": "^4.1.5", + "@smithy/types": "^3.6.0", + "@smithy/util-stream": "^3.2.1", "tslib": "^2.6.2" }, "engines": { @@ -3798,9 +4361,9 @@ } }, "node_modules/@smithy/types": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", - "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.6.0.tgz", + "integrity": "sha512-8VXK/KzOHefoC65yRgCn5vG1cysPJjHnOVt9d0ybFQSmJgQj152vMn4EkYhGuaOmnnZvCPav/KnYyE6/KsNZ2w==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -3810,13 +4373,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", - "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.8.tgz", + "integrity": "sha512-4FdOhwpTW7jtSFWm7SpfLGKIBC9ZaTKG5nBF0wK24aoQKQyDIKUw3+KFWCQ9maMzrgTJIuOvOnsV2lLGW5XjTg==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/querystring-parser": "^3.0.8", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" } }, @@ -3881,14 +4444,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz", - "integrity": "sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==", + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.25.tgz", + "integrity": "sha512-fRw7zymjIDt6XxIsLwfJfYUfbGoO9CmCJk6rjJ/X5cd20+d2Is7xjU5Kt/AiDt6hX8DAf5dztmfP5O82gR9emA==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/property-provider": "^3.1.8", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -3897,17 +4460,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz", - "integrity": "sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==", + "version": "3.0.25", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.25.tgz", + "integrity": "sha512-H3BSZdBDiVZGzt8TG51Pd2FvFO0PAx/A0mJ0EH8a13KJ6iUCdYnw/Dk/MdC1kTd0eUuUGisDFaxXVXo4HHFL1g==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^3.0.5", - "@smithy/credential-provider-imds": "^3.2.0", - "@smithy/node-config-provider": "^3.1.4", - "@smithy/property-provider": "^3.1.3", - "@smithy/smithy-client": "^3.2.0", - "@smithy/types": "^3.3.0", + "@smithy/config-resolver": "^3.0.10", + "@smithy/credential-provider-imds": "^3.2.5", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/property-provider": "^3.1.8", + "@smithy/smithy-client": "^3.4.2", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3915,13 +4478,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", - "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.1.4.tgz", + "integrity": "sha512-kPt8j4emm7rdMWQyL0F89o92q10gvCUa6sBkBtDJ7nV2+P7wpXczzOfoDJ49CKXe5CCqb8dc1W+ZdLlrKzSAnQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/node-config-provider": "^3.1.9", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3941,12 +4504,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", - "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.8.tgz", + "integrity": "sha512-p7iYAPaQjoeM+AKABpYWeDdtwQNxasr4aXQEA/OmbOaug9V0odRVDy3Wx4ci8soljE/JXQo+abV0qZpW8NX0yA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^3.3.0", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3954,13 +4517,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", - "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.8.tgz", + "integrity": "sha512-TCEhLnY581YJ+g1x0hapPz13JFqzmh/pMWL2KEFASC51qCfw3+Y47MrTmea4bUE5vsdxQ4F6/KFbUeSz22Q1ow==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^3.0.3", - "@smithy/types": "^3.3.0", + "@smithy/service-error-classification": "^3.0.8", + "@smithy/types": "^3.6.0", "tslib": "^2.6.2" }, "engines": { @@ -3968,14 +4531,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", - "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.2.1.tgz", + "integrity": "sha512-R3ufuzJRxSJbE58K9AEnL/uSZyVdHzud9wLS8tIbXclxKzoe09CRohj2xV8wpx5tj7ZbiJaKYcutMm1eYgz/0A==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^3.2.4", - "@smithy/node-http-handler": "^3.1.4", - "@smithy/types": "^3.3.0", + "@smithy/fetch-http-handler": "^4.0.0", + "@smithy/node-http-handler": "^3.2.5", + "@smithy/types": "^3.6.0", "@smithy/util-base64": "^3.0.0", "@smithy/util-buffer-from": "^3.0.0", "@smithy/util-hex-encoding": "^3.0.0", @@ -3986,6 +4549,19 @@ "node": ">=16.0.0" } }, + "node_modules/@smithy/util-stream/node_modules/@smithy/fetch-http-handler": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-4.0.0.tgz", + "integrity": "sha512-MLb1f5tbBO2X6K4lMEKJvxeLooyg7guq48C2zKr4qM7F2Gpkz4dc+hdSgu77pCJ76jVqFBjZczHYAs6dp15N+g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^4.1.5", + "@smithy/querystring-builder": "^3.0.8", + "@smithy/types": "^3.6.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, "node_modules/@smithy/util-uri-escape": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", @@ -8858,6 +9434,10 @@ "node": ">=8" } }, + "node_modules/poller-lambdas": { + "resolved": "poller-lambdas", + "link": true + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -10456,6 +11036,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "poller-lambdas": { + "version": "1.0.0", + "dependencies": { + "@aws-sdk/client-sqs": "^3.682.0" + }, + "devDependencies": { + "@types/aws-lambda": "8.10.145" + } } } } diff --git a/package.json b/package.json index 2122cc33..340d477b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "Newswires feed app", "workspaces": [ "cdk", - "ingestion-lambda" + "ingestion-lambda", + "poller-lambdas" ], "scripts": { "test": "npm run test --workspaces --if-present", diff --git a/poller-lambdas/package.json b/poller-lambdas/package.json new file mode 100644 index 00000000..70f8418b --- /dev/null +++ b/poller-lambdas/package.json @@ -0,0 +1,16 @@ +{ + "name": "poller-lambdas", + "version": "1.0.0", + "description": "", + "scripts": { + "dev": "tsx watch localRun.ts", + "typecheck": "tsc -noEmit", + "build": "esbuild src/index.ts --bundle --minify --outfile=dist/index.js --external:@aws-sdk --external:aws-sdk --platform=node" + }, + "dependencies": { + }, + "devDependencies": { + "@aws-sdk/client-sqs": "^3.682.0", + "@types/aws-lambda": "8.10.145" + } +} diff --git a/poller-lambdas/src/index.ts b/poller-lambdas/src/index.ts new file mode 100644 index 00000000..7f9e4803 --- /dev/null +++ b/poller-lambdas/src/index.ts @@ -0,0 +1,79 @@ +import { SQSEvent } from 'aws-lambda'; +import { SendMessageCommand, SQSClient } from '@aws-sdk/client-sqs'; +import { POLLER_LAMBDA_ENV_VAR_KEYS, PollerId } from '../../shared/pollers'; +import { PollFunction } from './types'; +import EXAMPLE_long_polling from './pollers/EXAMPLE_long_polling'; +import EXAMPLE_fixed_frequency from './pollers/EXAMPLE_fixed_frequency'; + +const getEnvironmentVariableOrCrash = ( + key: keyof typeof POLLER_LAMBDA_ENV_VAR_KEYS, +) => process.env[key]!; + +const sqs = new SQSClient({}); +const ownQueueUrl = getEnvironmentVariableOrCrash( + POLLER_LAMBDA_ENV_VAR_KEYS.OWN_QUEUE_URL, +); + +const pollerWrapper = + (pollerFunction: PollFunction) => + async ({ Records }: SQSEvent) => { + const startTimeEpochMillis = Date.now(); + const secret = 'TODO'; //TODO get secret (using name from env var) + if (Records.length != 1) { + console.warn('Expected exactly one SQS record, but got', Records.length); + } + return Promise.all( + Records.map((record) => { + const valueFromPreviousPoll = record.body; //TODO maybe parse + return pollerFunction(secret, valueFromPreviousPoll) + .then(async (output) => { + const endTimeEpochMillis = Date.now(); + + await sqs.send( + new SendMessageCommand({ + //TODO consider deduplication ID based on the unique story id from the agency?? + QueueUrl: getEnvironmentVariableOrCrash( + POLLER_LAMBDA_ENV_VAR_KEYS.INGESTION_LAMBDA_QUEUE_URL, + ), + MessageBody: output.jsonStringForIngestionLambda, + }), + ); // TODO wrap in try catch + + //TODO guard against too frequent polling + if ('valueForNextPoll' in output) { + await sqs.send( + new SendMessageCommand({ + QueueUrl: ownQueueUrl, + MessageBody: output.valueForNextPoll, + }), + ); + } else { + const remainingMillisBeforeNextInterval = + output.idealFrequencyInSeconds * 1000 - + (endTimeEpochMillis - startTimeEpochMillis); + const delayInSeconds = Math.max( + remainingMillisBeforeNextInterval / 1000, + 0, + ); + await sqs.send( + new SendMessageCommand({ + QueueUrl: ownQueueUrl, + DelaySeconds: delayInSeconds, + MessageBody: '', // TODO probably send something + }), + ); + } + }) + .catch((error) => { + console.error(error); + // TODO send message (perhaps with default delay or 1min) to avoid the lambda from stopping entirely + throw error; + }); + }), + ); + }; + +export = { + EXAMPLE_long_polling: pollerWrapper(EXAMPLE_long_polling), + EXAMPLE_fixed_frequency: pollerWrapper(EXAMPLE_fixed_frequency), +} satisfies Record Promise>; diff --git a/poller-lambdas/src/pollers/EXAMPLE_fixed_frequency.ts b/poller-lambdas/src/pollers/EXAMPLE_fixed_frequency.ts new file mode 100644 index 00000000..bb5a13e5 --- /dev/null +++ b/poller-lambdas/src/pollers/EXAMPLE_fixed_frequency.ts @@ -0,0 +1,9 @@ +import { FixedFrequencyPollFunction, SecretValue } from '../types'; + +export default (async (secret: SecretValue) => { + console.log('polling at ', new Date().toISOString()); + return { + jsonStringForIngestionLambda: JSON.stringify({}), //TODO move this stringify into index.ts + idealFrequencyInSeconds: 30, + }; +}) satisfies FixedFrequencyPollFunction; diff --git a/poller-lambdas/src/pollers/EXAMPLE_long_polling.ts b/poller-lambdas/src/pollers/EXAMPLE_long_polling.ts new file mode 100644 index 00000000..98092dc7 --- /dev/null +++ b/poller-lambdas/src/pollers/EXAMPLE_long_polling.ts @@ -0,0 +1,15 @@ +import { LongPollFunction, PollerInput, SecretValue } from '../types'; + +export default (async (secret: SecretValue, input: PollerInput) => { + const previousCounterValue = parseInt(input); + await new Promise((resolve) => setTimeout(resolve, 30000)); // simulate long poll taking 30s + const newCounterValue = previousCounterValue + 1; + console.log({ + previous: previousCounterValue, + newCounterValue: newCounterValue, + }); + return { + jsonStringForIngestionLambda: JSON.stringify({}), //TODO move this stringify into index.ts + valueForNextPoll: newCounterValue.toString(), + }; +}) satisfies LongPollFunction; diff --git a/poller-lambdas/src/types.ts b/poller-lambdas/src/types.ts new file mode 100644 index 00000000..6893fa84 --- /dev/null +++ b/poller-lambdas/src/types.ts @@ -0,0 +1,23 @@ +export type SecretValue = string; //TODO refine type + +export type PollerInput = string; + +export interface CorePollerOutput { + jsonStringForIngestionLambda: string; //TODO change to be the shared/agreed type the ingestion lambda expects then do JSON.stringify here +} +export type LongPollOutput = CorePollerOutput & { valueForNextPoll: string }; + +export type FixedFrequencyPollOutput = CorePollerOutput & { + idealFrequencyInSeconds: number; +}; + +export type LongPollFunction = ( + secret: SecretValue, + input: PollerInput, +) => Promise; + +export type FixedFrequencyPollFunction = ( + secret: SecretValue, +) => Promise; + +export type PollFunction = LongPollFunction | FixedFrequencyPollFunction; diff --git a/shared/pollers.ts b/shared/pollers.ts new file mode 100644 index 00000000..95a91cc5 --- /dev/null +++ b/shared/pollers.ts @@ -0,0 +1,20 @@ +export const POLLER_LAMBDA_ENV_VAR_KEYS = { + INGESTION_LAMBDA_QUEUE_URL: 'INGESTION_LAMBDA_QUEUE_URL', + OWN_QUEUE_URL: 'OWN_QUEUE_URL', +} as const; + +export interface PollerLambdaProps { + overrideLambdaMemoryMB?: number; + overrideLambdaTimeoutSeconds?: number; +} + +export type PollerConfig = PollerLambdaProps & { + idealFrequencyInSeconds?: number; //TODO some how guarantee that if this is not provided then the function must return something for the next invocation +}; + +export type PollerId = keyof typeof POLLERS_CONFIG; + +export const POLLERS_CONFIG = { + EXAMPLE_long_polling: {}, + EXAMPLE_fixed_frequency: { idealFrequencyInSeconds: 30 }, +} as const satisfies Record; // used to generate lambda etc. per agency, with config mapped