From 625c431334730cb7e5d429d39c5a586cc53771a8 Mon Sep 17 00:00:00 2001 From: Lee Date: Tue, 10 Dec 2024 19:44:46 +0000 Subject: [PATCH] feat(dynamodb): add precision timestamp for kinesis stream (#31863) ### Issue # (if applicable) Closes #31761 ### Reason for this change Missing feature for kinesis data streams ### Description of changes Added feature and unit tests ### Description of how you validated changes Unit tests and integ tests included ### 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* --------- Co-authored-by: Lee Hannigan --- ...ws-cdk-dynamodb-kinesis-stream.assets.json | 6 +-- ...-cdk-dynamodb-kinesis-stream.template.json | 1 + .../cdk.out | 2 +- .../integ.json | 2 +- .../manifest.json | 5 ++- .../tree.json | 43 ++++++++++--------- .../test/integ.dynamodb.kinesis-stream.ts | 1 + .../aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md | 2 + .../aws-cdk-lib/aws-dynamodb/lib/table.ts | 33 +++++++++++++- .../aws-dynamodb/test/dynamodb.test.ts | 32 ++++++++++++++ 10 files changed, 98 insertions(+), 29 deletions(-) diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/aws-cdk-dynamodb-kinesis-stream.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/aws-cdk-dynamodb-kinesis-stream.assets.json index 117bba8c0eebb..22e498389cf83 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/aws-cdk-dynamodb-kinesis-stream.assets.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/aws-cdk-dynamodb-kinesis-stream.assets.json @@ -1,7 +1,7 @@ { - "version": "36.0.0", + "version": "38.0.1", "files": { - "af9e3e690b2077e983a570a213e0aef3d3036ade5ae3fbb66fff5a3466ed4896": { + "671ebc97901116d0268136488a5ac9d7f9e9063f47eaeba1f894063342206593": { "source": { "path": "aws-cdk-dynamodb-kinesis-stream.template.json", "packaging": "file" @@ -9,7 +9,7 @@ "destinations": { "current_account-current_region": { "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", - "objectKey": "af9e3e690b2077e983a570a213e0aef3d3036ade5ae3fbb66fff5a3466ed4896.json", + "objectKey": "671ebc97901116d0268136488a5ac9d7f9e9063f47eaeba1f894063342206593.json", "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" } } diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/aws-cdk-dynamodb-kinesis-stream.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/aws-cdk-dynamodb-kinesis-stream.template.json index 8f793a1f32a44..d1110c367ec37 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/aws-cdk-dynamodb-kinesis-stream.template.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/aws-cdk-dynamodb-kinesis-stream.template.json @@ -37,6 +37,7 @@ } ], "KinesisStreamSpecification": { + "ApproximateCreationDateTimePrecision": "MILLISECOND", "StreamArn": { "Fn::GetAtt": [ "Stream790BDEE4", diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/cdk.out index 1f0068d32659a..c6e612584e352 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/cdk.out +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/cdk.out @@ -1 +1 @@ -{"version":"36.0.0"} \ No newline at end of file +{"version":"38.0.1"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/integ.json index 9150f34ef20c1..2c01431d8f7a5 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/integ.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/integ.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "38.0.1", "testCases": { "integ.dynamodb.kinesis-stream": { "stacks": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/manifest.json index 7adfbd49ba099..01aa7c58bbdc2 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/manifest.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/manifest.json @@ -1,5 +1,5 @@ { - "version": "36.0.0", + "version": "38.0.1", "artifacts": { "aws-cdk-dynamodb-kinesis-stream.assets": { "type": "cdk:asset-manifest", @@ -16,9 +16,10 @@ "templateFile": "aws-cdk-dynamodb-kinesis-stream.template.json", "terminationProtection": false, "validateOnSynth": false, + "notificationArns": [], "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}/af9e3e690b2077e983a570a213e0aef3d3036ade5ae3fbb66fff5a3466ed4896.json", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/671ebc97901116d0268136488a5ac9d7f9e9063f47eaeba1f894063342206593.json", "requiresBootstrapStackVersion": 6, "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", "additionalDependencies": [ diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/tree.json index a9b7d99624a87..839f5090d66ba 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/tree.json +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.js.snapshot/tree.json @@ -35,22 +35,22 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kinesis.CfnStream", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_kinesis.Stream", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "AwsCdkKinesisEncryptedStreamsUnsupportedRegions": { "id": "AwsCdkKinesisEncryptedStreamsUnsupportedRegions", "path": "aws-cdk-dynamodb-kinesis-stream/AwsCdkKinesisEncryptedStreamsUnsupportedRegions", "constructInfo": { - "fqn": "aws-cdk-lib.CfnCondition", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "Table": { @@ -81,7 +81,8 @@ "Stream790BDEE4", "Arn" ] - } + }, + "approximateCreationDateTimePrecision": "MILLISECOND" }, "provisionedThroughput": { "readCapacityUnits": 5, @@ -90,44 +91,44 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_dynamodb.CfnTable", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "ScalingRole": { "id": "ScalingRole", "path": "aws-cdk-dynamodb-kinesis-stream/Table/ScalingRole", "constructInfo": { - "fqn": "aws-cdk-lib.Resource", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.aws_dynamodb.Table", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "BootstrapVersion": { "id": "BootstrapVersion", "path": "aws-cdk-dynamodb-kinesis-stream/BootstrapVersion", "constructInfo": { - "fqn": "aws-cdk-lib.CfnParameter", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "CheckBootstrapVersion": { "id": "CheckBootstrapVersion", "path": "aws-cdk-dynamodb-kinesis-stream/CheckBootstrapVersion", "constructInfo": { - "fqn": "aws-cdk-lib.CfnRule", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } }, "constructInfo": { - "fqn": "aws-cdk-lib.Stack", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } }, "Tree": { @@ -140,8 +141,8 @@ } }, "constructInfo": { - "fqn": "aws-cdk-lib.App", - "version": "0.0.0" + "fqn": "constructs.Construct", + "version": "10.3.0" } } } \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.ts index f403d06816211..8fb9cfdd4a94f 100644 --- a/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.ts +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-dynamodb/test/integ.dynamodb.kinesis-stream.ts @@ -11,6 +11,7 @@ new dynamodb.Table(stack, 'Table', { partitionKey: { name: 'hashKey', type: dynamodb.AttributeType.STRING }, removalPolicy: cdk.RemovalPolicy.DESTROY, kinesisStream: stream, + kinesisPrecisionTimestamp: dynamodb.ApproximateCreationDateTimePrecision.MILLISECOND, }); app.synth(); diff --git a/packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md b/packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md index 9d62a170a3676..e316b314d184a 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md +++ b/packages/aws-cdk-lib/aws-dynamodb/TABLE_V1_API.md @@ -222,6 +222,8 @@ const sortKey = schema.sortKey; A Kinesis Data Stream can be configured on the DynamoDB table to capture item-level changes. +You can optionally configure the `kinesisPrecisionTimestamp` parameter to specify the precision level of the approximate creation date and time. The allowed values are `MICROSECOND` and `MILLISECOND`. If this parameter is not specified, the default precision is set to `MICROSECOND`. + ```ts import * as kinesis from 'aws-cdk-lib/aws-kinesis'; diff --git a/packages/aws-cdk-lib/aws-dynamodb/lib/table.ts b/packages/aws-cdk-lib/aws-dynamodb/lib/table.ts index a020bf33b0011..55ed1082a717a 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/lib/table.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/lib/table.ts @@ -208,6 +208,23 @@ export interface ImportSourceSpecification { readonly keyPrefix?: string; } +/** + * The precision associated with the DynamoDB write timestamps that will be replicated to Kinesis. + * The default setting for record timestamp precision is microseconds. You can change this setting at any time. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-dynamodb-table-kinesisstreamspecification.html#aws-properties-dynamodb-table-kinesisstreamspecification-properties + */ +export enum ApproximateCreationDateTimePrecision { + /** + * Millisecond precision + */ + MILLISECOND = 'MILLISECOND', + + /** + * Microsecond precision + */ + MICROSECOND = 'MICROSECOND', +} + /** * Properties of a DynamoDB Table * @@ -423,6 +440,13 @@ export interface TableProps extends TableOptions { * @default - no Kinesis Data Stream */ readonly kinesisStream?: kinesis.IStream; + + /** + * Kinesis Data Stream approximate creation timestamp prescision + * + * @default ApproximateCreationDateTimePrecision.MICROSECOND + */ + readonly kinesisPrecisionTimestamp?: ApproximateCreationDateTimePrecision; } /** @@ -1172,6 +1196,13 @@ export class Table extends TableBase { } this.validateProvisioning(props); + const kinesisStreamSpecification = props.kinesisStream + ? { + streamArn: props.kinesisStream.streamArn, + ...(props.kinesisPrecisionTimestamp && { approximateCreationDateTimePrecision: props.kinesisPrecisionTimestamp }), + } + : undefined; + this.table = new CfnTable(this, 'Resource', { tableName: this.physicalName, keySchema: this.keySchema, @@ -1196,7 +1227,7 @@ export class Table extends TableBase { tableClass: props.tableClass, timeToLiveSpecification: props.timeToLiveAttribute ? { attributeName: props.timeToLiveAttribute, enabled: true } : undefined, contributorInsightsSpecification: props.contributorInsightsEnabled !== undefined ? { enabled: props.contributorInsightsEnabled } : undefined, - kinesisStreamSpecification: props.kinesisStream ? { streamArn: props.kinesisStream.streamArn } : undefined, + kinesisStreamSpecification: kinesisStreamSpecification, deletionProtectionEnabled: props.deletionProtection, importSourceSpecification: this.renderImportSourceSpecification(props.importSource), resourcePolicy: props.resourcePolicy diff --git a/packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts b/packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts index 0a95fea266ea3..d190bbb14f622 100644 --- a/packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts +++ b/packages/aws-cdk-lib/aws-dynamodb/test/dynamodb.test.ts @@ -24,6 +24,7 @@ import { CfnTable, InputCompressionType, InputFormat, + ApproximateCreationDateTimePrecision, } from '../lib'; import { ReplicaProvider } from '../lib/replica-provider'; @@ -3783,3 +3784,34 @@ test('Warm Throughput test provisioned', () => { }); }); + +test('Kinesis Stream - precision timestamp', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'Stack'); + + const stream = new kinesis.Stream(stack, 'Stream'); + + // WHEN + const table = new Table(stack, 'Table', { + partitionKey: { name: 'id', type: AttributeType.STRING }, + kinesisStream: stream, + kinesisPrecisionTimestamp: ApproximateCreationDateTimePrecision.MILLISECOND, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::DynamoDB::Table', { + KeySchema: [ + { AttributeName: 'id', KeyType: 'HASH' }, + ], + AttributeDefinitions: [ + { AttributeName: 'id', AttributeType: 'S' }, + ], + KinesisStreamSpecification: { + StreamArn: { + 'Fn::GetAtt': ['Stream790BDEE4', 'Arn'], + }, + ApproximateCreationDateTimePrecision: 'MILLISECOND', + }, + }); +});