From 9946ab03672bf6664e8ec95a81ddb67c3bb2f63b Mon Sep 17 00:00:00 2001 From: Melle van der Linde <118454433+mellevanderlinde@users.noreply.github.com> Date: Wed, 28 Aug 2024 03:09:06 +0200 Subject: [PATCH] fix(lambda-event-source): allow dynamodb filtering on boolean value (#31011) ### Issue # (if applicable) Closes #30734 ### Reason for this change Filtering on boolean values was not possible ### Description of changes `lambda.FilterRule.isEqual(...)` now accepts `boolean` values ### Description of how you validated changes Added a unit test and integration test ### Checklist - [x] My code adheres to the [CONTRIBUTING GUIDE](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) and [DESIGN GUIDELINES](https://github.com/aws/aws-cdk/blob/main/docs/DESIGN_GUIDELINES.md) ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssert8561382F.assets.json | 19 + ...aultTestDeployAssert8561382F.template.json | 36 ++ .../cdk.out | 1 + .../integ.json | 12 + ...source-filter-boolean-dynamodb.assets.json | 19 + ...urce-filter-boolean-dynamodb.template.json | 173 +++++++++ .../manifest.json | 137 +++++++ .../tree.json | 336 ++++++++++++++++++ .../integ.dynamodb-with-boolean-filter.ts | 41 +++ .../aws-lambda-event-sources/README.md | 25 ++ .../test/dynamo.test.ts | 49 +++ .../aws-lambda/lib/event-source-filter.ts | 2 +- 12 files changed, 849 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/lambda-event-source-filter-boolean-dynamodb.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/lambda-event-source-filter-boolean-dynamodb.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.assets.json new file mode 100644 index 0000000000000..52b45419dad0a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/integ.json new file mode 100644 index 0000000000000..827da5824ceba --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "DynamoDBFilterBoolean/DefaultTest": { + "stacks": [ + "lambda-event-source-filter-boolean-dynamodb" + ], + "assertionStack": "DynamoDBFilterBoolean/DefaultTest/DeployAssert", + "assertionStackName": "DynamoDBFilterBooleanDefaultTestDeployAssert8561382F" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/lambda-event-source-filter-boolean-dynamodb.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/lambda-event-source-filter-boolean-dynamodb.assets.json new file mode 100644 index 0000000000000..8b90b83d51bb6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/lambda-event-source-filter-boolean-dynamodb.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "f60708303fa3fc942070d172c3d3b758824b55d89bee7dc151ebe2968221b264": { + "source": { + "path": "lambda-event-source-filter-boolean-dynamodb.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "f60708303fa3fc942070d172c3d3b758824b55d89bee7dc151ebe2968221b264.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/lambda-event-source-filter-boolean-dynamodb.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/lambda-event-source-filter-boolean-dynamodb.template.json new file mode 100644 index 0000000000000..e40b18f46a104 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/lambda-event-source-filter-boolean-dynamodb.template.json @@ -0,0 +1,173 @@ +{ + "Resources": { + "FServiceRole3AC82EE1": { + "Type": "AWS::IAM::Role", + "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" + ] + ] + } + ] + } + }, + "FServiceRoleDefaultPolicy17A19BFA": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyDocument": { + "Statement": [ + { + "Action": "dynamodb:ListStreams", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:DescribeStream", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TD925BC7E", + "StreamArn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "PolicyName": "FServiceRoleDefaultPolicy17A19BFA", + "Roles": [ + { + "Ref": "FServiceRole3AC82EE1" + } + ] + } + }, + "FC4345940": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "ZipFile": "exports.handler = async function handler(event) {\n console.log('event:', JSON.stringify(event, undefined, 2));\n return { event };\n}" + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "FServiceRole3AC82EE1", + "Arn" + ] + }, + "Runtime": "nodejs18.x" + }, + "DependsOn": [ + "FServiceRoleDefaultPolicy17A19BFA", + "FServiceRole3AC82EE1" + ] + }, + "FDynamoDBEventSourcelambdaeventsourcefilterbooleandynamodbT2161ED824BB5B64C": { + "Type": "AWS::Lambda::EventSourceMapping", + "Properties": { + "BatchSize": 5, + "EventSourceArn": { + "Fn::GetAtt": [ + "TD925BC7E", + "StreamArn" + ] + }, + "FilterCriteria": { + "Filters": [ + { + "Pattern": "{\"eventName\":[\"INSERT\"],\"dynamodb\":{\"NewImage\":{\"id\":{\"BOOL\":[true]}}}}" + } + ] + }, + "FunctionName": { + "Ref": "FC4345940" + }, + "StartingPosition": "LATEST" + } + }, + "TD925BC7E": { + "Type": "AWS::DynamoDB::Table", + "Properties": { + "AttributeDefinitions": [ + { + "AttributeName": "id", + "AttributeType": "S" + } + ], + "KeySchema": [ + { + "AttributeName": "id", + "KeyType": "HASH" + } + ], + "ProvisionedThroughput": { + "ReadCapacityUnits": 5, + "WriteCapacityUnits": 5 + }, + "StreamSpecification": { + "StreamViewType": "NEW_IMAGE" + } + }, + "UpdateReplacePolicy": "Delete", + "DeletionPolicy": "Delete" + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/manifest.json new file mode 100644 index 0000000000000..e42c90c7abd03 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/manifest.json @@ -0,0 +1,137 @@ +{ + "version": "36.0.0", + "artifacts": { + "lambda-event-source-filter-boolean-dynamodb.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "lambda-event-source-filter-boolean-dynamodb.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "lambda-event-source-filter-boolean-dynamodb": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "lambda-event-source-filter-boolean-dynamodb.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/f60708303fa3fc942070d172c3d3b758824b55d89bee7dc151ebe2968221b264.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "lambda-event-source-filter-boolean-dynamodb.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "lambda-event-source-filter-boolean-dynamodb.assets" + ], + "metadata": { + "/lambda-event-source-filter-boolean-dynamodb/F/ServiceRole/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FServiceRole3AC82EE1" + } + ], + "/lambda-event-source-filter-boolean-dynamodb/F/ServiceRole/DefaultPolicy/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FServiceRoleDefaultPolicy17A19BFA" + } + ], + "/lambda-event-source-filter-boolean-dynamodb/F/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FC4345940" + } + ], + "/lambda-event-source-filter-boolean-dynamodb/F/DynamoDBEventSource:lambdaeventsourcefilterbooleandynamodbT2161ED82/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "FDynamoDBEventSourcelambdaeventsourcefilterbooleandynamodbT2161ED824BB5B64C" + } + ], + "/lambda-event-source-filter-boolean-dynamodb/T/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "TD925BC7E" + } + ], + "/lambda-event-source-filter-boolean-dynamodb/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/lambda-event-source-filter-boolean-dynamodb/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "lambda-event-source-filter-boolean-dynamodb" + }, + "DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "DynamoDBFilterBooleanDefaultTestDeployAssert8561382F": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "DynamoDBFilterBooleanDefaultTestDeployAssert8561382F.assets" + ], + "metadata": { + "/DynamoDBFilterBoolean/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/DynamoDBFilterBoolean/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "DynamoDBFilterBoolean/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/tree.json new file mode 100644 index 0000000000000..e107666a6a5da --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.js.snapshot/tree.json @@ -0,0 +1,336 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "lambda-event-source-filter-boolean-dynamodb": { + "id": "lambda-event-source-filter-boolean-dynamodb", + "path": "lambda-event-source-filter-boolean-dynamodb", + "children": { + "F": { + "id": "F", + "path": "lambda-event-source-filter-boolean-dynamodb/F", + "children": { + "ServiceRole": { + "id": "ServiceRole", + "path": "lambda-event-source-filter-boolean-dynamodb/F/ServiceRole", + "children": { + "ImportServiceRole": { + "id": "ImportServiceRole", + "path": "lambda-event-source-filter-boolean-dynamodb/F/ServiceRole/ImportServiceRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-boolean-dynamodb/F/ServiceRole/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Role", + "aws:cdk:cloudformation:props": { + "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" + ] + ] + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnRole", + "version": "0.0.0" + } + }, + "DefaultPolicy": { + "id": "DefaultPolicy", + "path": "lambda-event-source-filter-boolean-dynamodb/F/ServiceRole/DefaultPolicy", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-boolean-dynamodb/F/ServiceRole/DefaultPolicy/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::IAM::Policy", + "aws:cdk:cloudformation:props": { + "policyDocument": { + "Statement": [ + { + "Action": "dynamodb:ListStreams", + "Effect": "Allow", + "Resource": "*" + }, + { + "Action": [ + "dynamodb:DescribeStream", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator" + ], + "Effect": "Allow", + "Resource": { + "Fn::GetAtt": [ + "TD925BC7E", + "StreamArn" + ] + } + } + ], + "Version": "2012-10-17" + }, + "policyName": "FServiceRoleDefaultPolicy17A19BFA", + "roles": [ + { + "Ref": "FServiceRole3AC82EE1" + } + ] + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.CfnPolicy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Policy", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_iam.Role", + "version": "0.0.0" + } + }, + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-boolean-dynamodb/F/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::Function", + "aws:cdk:cloudformation:props": { + "code": { + "zipFile": "exports.handler = async function handler(event) {\n console.log('event:', JSON.stringify(event, undefined, 2));\n return { event };\n}" + }, + "handler": "index.handler", + "role": { + "Fn::GetAtt": [ + "FServiceRole3AC82EE1", + "Arn" + ] + }, + "runtime": "nodejs18.x" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnFunction", + "version": "0.0.0" + } + }, + "DynamoDBEventSource:lambdaeventsourcefilterbooleandynamodbT2161ED82": { + "id": "DynamoDBEventSource:lambdaeventsourcefilterbooleandynamodbT2161ED82", + "path": "lambda-event-source-filter-boolean-dynamodb/F/DynamoDBEventSource:lambdaeventsourcefilterbooleandynamodbT2161ED82", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-boolean-dynamodb/F/DynamoDBEventSource:lambdaeventsourcefilterbooleandynamodbT2161ED82/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::Lambda::EventSourceMapping", + "aws:cdk:cloudformation:props": { + "batchSize": 5, + "eventSourceArn": { + "Fn::GetAtt": [ + "TD925BC7E", + "StreamArn" + ] + }, + "filterCriteria": { + "filters": [ + { + "pattern": "{\"eventName\":[\"INSERT\"],\"dynamodb\":{\"NewImage\":{\"id\":{\"BOOL\":[true]}}}}" + } + ] + }, + "functionName": { + "Ref": "FC4345940" + }, + "startingPosition": "LATEST" + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.CfnEventSourceMapping", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.EventSourceMapping", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_lambda.Function", + "version": "0.0.0" + } + }, + "T": { + "id": "T", + "path": "lambda-event-source-filter-boolean-dynamodb/T", + "children": { + "Resource": { + "id": "Resource", + "path": "lambda-event-source-filter-boolean-dynamodb/T/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::DynamoDB::Table", + "aws:cdk:cloudformation:props": { + "attributeDefinitions": [ + { + "attributeName": "id", + "attributeType": "S" + } + ], + "keySchema": [ + { + "attributeName": "id", + "keyType": "HASH" + } + ], + "provisionedThroughput": { + "readCapacityUnits": 5, + "writeCapacityUnits": 5 + }, + "streamSpecification": { + "streamViewType": "NEW_IMAGE" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.CfnTable", + "version": "0.0.0" + } + }, + "ScalingRole": { + "id": "ScalingRole", + "path": "lambda-event-source-filter-boolean-dynamodb/T/ScalingRole", + "constructInfo": { + "fqn": "aws-cdk-lib.Resource", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_dynamodb.Table", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "lambda-event-source-filter-boolean-dynamodb/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "lambda-event-source-filter-boolean-dynamodb/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "DynamoDBFilterBoolean": { + "id": "DynamoDBFilterBoolean", + "path": "DynamoDBFilterBoolean", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "DynamoDBFilterBoolean/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "DynamoDBFilterBoolean/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "DynamoDBFilterBoolean/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "DynamoDBFilterBoolean/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "DynamoDBFilterBoolean/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.ts new file mode 100644 index 0000000000000..2de0fea3e09df --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-lambda-event-sources/test/integ.dynamodb-with-boolean-filter.ts @@ -0,0 +1,41 @@ +import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as cdk from 'aws-cdk-lib'; +import * as integ from '@aws-cdk/integ-tests-alpha'; +import { TestFunction } from './test-function'; +import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; + +const app = new cdk.App(); + +const stack = new cdk.Stack(app, 'lambda-event-source-filter-boolean-dynamodb'); + +const fn = new TestFunction(stack, 'F'); +const table = new dynamodb.Table(stack, 'T', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + stream: dynamodb.StreamViewType.NEW_IMAGE, + removalPolicy: cdk.RemovalPolicy.DESTROY, +}); + +fn.addEventSource(new DynamoEventSource(table, { + batchSize: 5, + startingPosition: lambda.StartingPosition.LATEST, + filters: [ + lambda.FilterCriteria.filter({ + eventName: lambda.FilterRule.isEqual('INSERT'), + dynamodb: { + NewImage: { + id: { BOOL: lambda.FilterRule.isEqual(true) }, + }, + }, + }), + ], +})); + +new integ.IntegTest(app, 'DynamoDBFilterBoolean', { + testCases: [stack], +}); + +app.synth(); diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/README.md b/packages/aws-cdk-lib/aws-lambda-event-sources/README.md index 8c88ae99bc8b8..6ad5ebe73df0e 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/README.md +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/README.md @@ -169,6 +169,7 @@ and add it to your Lambda function. The following parameters will impact Amazon * __startingPosition__: Will determine where to being consumption, either at the most recent ('LATEST') record or the oldest record ('TRIM_HORIZON'). 'TRIM_HORIZON' will ensure you process all available data, while 'LATEST' will ignore all records that arrived prior to attaching the event source. * __tumblingWindow__: The duration in seconds of a processing window when using streams. * __enabled__: If the DynamoDB Streams event source mapping should be enabled. The default is true. +* __filters__: Filters to apply before sending a change event from a DynamoDB table to a Lambda function. Events that are filtered out are not sent to the Lambda function. ```ts import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; @@ -188,6 +189,30 @@ fn.addEventSource(new DynamoEventSource(table, { })); ``` +The following code sets up a Lambda function with a DynamoDB event source. A filter is applied to only send DynamoDB events to +the Lambda function when the `id` column is a boolean that equals `true`. + +```ts +import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; +import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources'; + +declare const table: dynamodb.Table; + +declare const fn: lambda.Function; +fn.addEventSource(new DynamoEventSource(table, { + startingPosition: lambda.StartingPosition.LATEST, + filters: [ + lambda.FilterCriteria.filter({ + eventName: lambda.FilterRule.isEqual('INSERT'), + dynamodb: { + NewImage: { + id: { BOOL: lambda.FilterRule.isEqual(true) }, + }, + }, + }), + ], +})); +``` ## Kinesis You can write Lambda functions to process streaming data in Amazon Kinesis Streams. For more information about Amazon Kinesis, see [Amazon Kinesis diff --git a/packages/aws-cdk-lib/aws-lambda-event-sources/test/dynamo.test.ts b/packages/aws-cdk-lib/aws-lambda-event-sources/test/dynamo.test.ts index eb331b0de8972..92185d565598b 100644 --- a/packages/aws-cdk-lib/aws-lambda-event-sources/test/dynamo.test.ts +++ b/packages/aws-cdk-lib/aws-lambda-event-sources/test/dynamo.test.ts @@ -949,4 +949,53 @@ describe('DynamoEventSource', () => { }).toThrowError('S3 onFailure Destination is not supported for this event source'); }); + + test('filter on boolean', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = new TestFunction(stack, 'Fn'); + const table = new dynamodb.Table(stack, 'T', { + partitionKey: { + name: 'id', + type: dynamodb.AttributeType.STRING, + }, + stream: dynamodb.StreamViewType.NEW_IMAGE, + }); + + // WHEN + fn.addEventSource(new sources.DynamoEventSource(table, { + startingPosition: lambda.StartingPosition.LATEST, + filters: [ + lambda.FilterCriteria.filter({ + eventName: lambda.FilterRule.isEqual('INSERT'), + dynamodb: { + NewImage: { + id: { BOOL: lambda.FilterRule.isEqual(true) }, + }, + }, + }), + ], + })); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', { + 'EventSourceArn': { + 'Fn::GetAtt': [ + 'TD925BC7E', + 'StreamArn', + ], + }, + 'FunctionName': { + 'Ref': 'Fn9270CBC0', + }, + 'FilterCriteria': { + 'Filters': [ + { + 'Pattern': '{"eventName":["INSERT"],"dynamodb":{"NewImage":{"id":{"BOOL":[true]}}}}', + }, + ], + }, + 'StartingPosition': 'LATEST', + }); + }); }); diff --git a/packages/aws-cdk-lib/aws-lambda/lib/event-source-filter.ts b/packages/aws-cdk-lib/aws-lambda/lib/event-source-filter.ts index 50bce839123ba..a20bcedf4d82a 100644 --- a/packages/aws-cdk-lib/aws-lambda/lib/event-source-filter.ts +++ b/packages/aws-cdk-lib/aws-lambda/lib/event-source-filter.ts @@ -19,7 +19,7 @@ export class FilterRule { /** * Equals comparison operator */ - public static isEqual(item: string | number): any { + public static isEqual(item: string | number | boolean): any { if (typeof item === 'number') { return [{ numeric: ['=', item] }]; }