From 4ecb29cfe7081626c3cc70746078fbd40617265c Mon Sep 17 00:00:00 2001 From: Peter Myers Date: Wed, 14 May 2025 14:27:17 -0400 Subject: [PATCH 1/3] Prefer event timestamp in ddb trigger events. --- package-lock.json | 4 +- package.json | 2 +- src/from/dynamodb.js | 5 +- test/unit/from/dynamodb.test.js | 106 ++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d1d4868..9b34f465 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "aws-lambda-stream", - "version": "1.0.34", + "version": "1.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aws-lambda-stream", - "version": "1.0.34", + "version": "1.1.0", "license": "MIT", "dependencies": { "object-sizeof": "^2.6.0" diff --git a/package.json b/package.json index 51f0f3f5..dbb144fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aws-lambda-stream", - "version": "1.0.34", + "version": "1.1.0", "description": "Create stream processors with AWS Lambda functions.", "keywords": [ "aws", diff --git a/src/from/dynamodb.js b/src/from/dynamodb.js index f17679c8..57335d11 100644 --- a/src/from/dynamodb.js +++ b/src/from/dynamodb.js @@ -34,7 +34,7 @@ export const fromDynamodb = (event, { id: record.eventID, type: `${calculateEventTypePrefix(record, { skFn, discriminatorFn, eventTypePrefix })}-${calculateEventTypeSuffix(record)}`, partitionKey: record.dynamodb.Keys[pkFn].S, - timestamp: record.dynamodb.ApproximateCreationDateTime * 1000, + timestamp: deriveTimestamp(record), tags: { region: record.awsRegion, }, @@ -88,6 +88,9 @@ const calculateEventTypeSuffix = (record) => { return suffix; }; +const deriveTimestamp = (record) => + record.dynamodb.NewImage?.timestamp || (record.dynamodb.ApproximateCreationDateTime * 1000); + //-------------------------------------------- // global table support - version: 2017.11.29 //-------------------------------------------- diff --git a/test/unit/from/dynamodb.test.js b/test/unit/from/dynamodb.test.js index 28373879..434a4c25 100644 --- a/test/unit/from/dynamodb.test.js +++ b/test/unit/from/dynamodb.test.js @@ -111,6 +111,112 @@ describe('from/dynamodb.js', () => { .done(done); }); + it('should prefer image timestamp if present on INSERT', (done) => { + const events = toDynamodbRecords([ + { + timestamp: 1572832690, + keys: { + pk: '1', + sk: 'thing', + }, + newImage: { + pk: '1', + sk: 'thing', + discriminator: 'thing', + name: 'n1', + timestamp: 1572832690001 + // insert in the current region will not have the awsregion field + }, + }, + // dynamodb stream emits an extra update event as it adorns the 'aws:rep' global table metadata + // so this extra event should be skipped + { + timestamp: 1572832690, + keys: { + pk: '1', + sk: 'thing', + }, + newImage: { + pk: '1', + sk: 'thing', + discriminator: 'thing', + name: 'n1', + awsregion: 'us-west-2', + }, + oldImage: { + pk: '1', + sk: 'thing', + discriminator: 'thing', + name: 'n1', + // as mentioned above there was no awsregion field on the insert event + }, + }, + ]); + + fromDynamodb(events) + .collect() + .tap((collected) => { + // console.log(JSON.stringify(collected, null, 2)); + + expect(collected.length).to.equal(1); + expect(collected[0]).to.deep.equal({ + record: { + eventID: '0', + eventName: 'INSERT', + eventSource: 'aws:dynamodb', + awsRegion: 'us-west-2', + dynamodb: { + ApproximateCreationDateTime: 1572832690, + Keys: { + pk: { + S: '1', + }, + sk: { + S: 'thing', + }, + }, + NewImage: { + pk: { + S: '1', + }, + sk: { + S: 'thing', + }, + discriminator: { + S: 'thing', + }, + name: { + S: 'n1', + }, + }, + OldImage: undefined, + SequenceNumber: '0', + StreamViewType: 'NEW_AND_OLD_IMAGES', + }, + }, + event: { + id: '0', + type: 'thing-created', + partitionKey: '1', + timestamp: 1572832690000, + tags: { + region: 'us-west-2', + }, + raw: { + new: { + pk: '1', + sk: 'thing', + discriminator: 'thing', + name: 'n1', + }, + old: undefined, + }, + }, + }); + }) + .done(done); + }); + it('should parse MODIFY record', (done) => { const events = toDynamodbRecords([ { From d37c36f82bcc43ac20401f92f15307c9b7a82570 Mon Sep 17 00:00:00 2001 From: Peter Myers Date: Wed, 14 May 2025 14:47:26 -0400 Subject: [PATCH 2/3] Fix + cleanup test. --- src/from/dynamodb.js | 5 ++++- test/unit/from/dynamodb.test.js | 19 ++++++++++++++++--- test/unit/utils/encryption.test.js | 4 ++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/from/dynamodb.js b/src/from/dynamodb.js index 57335d11..1d5e3212 100644 --- a/src/from/dynamodb.js +++ b/src/from/dynamodb.js @@ -35,6 +35,7 @@ export const fromDynamodb = (event, { type: `${calculateEventTypePrefix(record, { skFn, discriminatorFn, eventTypePrefix })}-${calculateEventTypeSuffix(record)}`, partitionKey: record.dynamodb.Keys[pkFn].S, timestamp: deriveTimestamp(record), + approximateCreationTimestamp: ddbApproximateCreationTimestamp(record), tags: { region: record.awsRegion, }, @@ -89,7 +90,9 @@ const calculateEventTypeSuffix = (record) => { }; const deriveTimestamp = (record) => - record.dynamodb.NewImage?.timestamp || (record.dynamodb.ApproximateCreationDateTime * 1000); + parseInt(record.dynamodb.NewImage?.timestamp?.N, 10) || ddbApproximateCreationTimestamp(record); + +const ddbApproximateCreationTimestamp = (record) => record.dynamodb.ApproximateCreationDateTime * 1000; //-------------------------------------------- // global table support - version: 2017.11.29 diff --git a/test/unit/from/dynamodb.test.js b/test/unit/from/dynamodb.test.js index 434a4c25..be7230ad 100644 --- a/test/unit/from/dynamodb.test.js +++ b/test/unit/from/dynamodb.test.js @@ -93,6 +93,7 @@ describe('from/dynamodb.js', () => { type: 'thing-created', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -111,7 +112,7 @@ describe('from/dynamodb.js', () => { .done(done); }); - it('should prefer image timestamp if present on INSERT', (done) => { + it('should prefer image timestamp if present', (done) => { const events = toDynamodbRecords([ { timestamp: 1572832690, @@ -124,7 +125,7 @@ describe('from/dynamodb.js', () => { sk: 'thing', discriminator: 'thing', name: 'n1', - timestamp: 1572832690001 + timestamp: 1572832690001, // insert in the current region will not have the awsregion field }, }, @@ -188,6 +189,9 @@ describe('from/dynamodb.js', () => { name: { S: 'n1', }, + timestamp: { + N: '1572832690001', + }, }, OldImage: undefined, SequenceNumber: '0', @@ -198,7 +202,8 @@ describe('from/dynamodb.js', () => { id: '0', type: 'thing-created', partitionKey: '1', - timestamp: 1572832690000, + timestamp: 1572832690001, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -208,6 +213,7 @@ describe('from/dynamodb.js', () => { sk: 'thing', discriminator: 'thing', name: 'n1', + timestamp: 1572832690001, }, old: undefined, }, @@ -334,6 +340,7 @@ describe('from/dynamodb.js', () => { type: 'thing-updated', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -440,6 +447,7 @@ describe('from/dynamodb.js', () => { type: 'thing-deleted', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -549,6 +557,7 @@ describe('from/dynamodb.js', () => { type: 'thing-deleted', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -673,6 +682,7 @@ describe('from/dynamodb.js', () => { type: 'thing-undeleted', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -771,6 +781,7 @@ describe('from/dynamodb.js', () => { type: 'thing-deleted', partitionKey: '1', timestamp: 1573005490000, + approximateCreationTimestamp: 1573005490000, tags: { region: 'us-west-2', }, @@ -948,6 +959,7 @@ describe('from/dynamodb.js', () => { type: 'thing-updated', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -1029,6 +1041,7 @@ describe('from/dynamodb.js', () => { type: 'thing-updated', partitionKey: '1', timestamp: 1572832990000, + approximateCreationTimestamp: 1572832990000, tags: { region: 'us-east-1', }, diff --git a/test/unit/utils/encryption.test.js b/test/unit/utils/encryption.test.js index b2e17b74..2489dfd6 100644 --- a/test/unit/utils/encryption.test.js +++ b/test/unit/utils/encryption.test.js @@ -62,6 +62,7 @@ describe('utils/encryption.js', () => { type: 'thing-created', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -394,6 +395,7 @@ describe('utils/encryption.js', () => { type: 'thing-created', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -475,6 +477,7 @@ describe('utils/encryption.js', () => { type: 'thing-updated', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -545,6 +548,7 @@ describe('utils/encryption.js', () => { type: 'thing-deleted', partitionKey: '1', timestamp: 1572832690000, + approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, From 6597e3c6efb5d6f09464dc56512ecec21ced802e Mon Sep 17 00:00:00 2001 From: Peter Myers Date: Thu, 15 May 2025 13:21:57 -0400 Subject: [PATCH 3/3] Remove approx ddb timestamp. --- src/from/dynamodb.js | 3 +-- test/unit/from/dynamodb.test.js | 9 --------- test/unit/utils/encryption.test.js | 4 ---- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/from/dynamodb.js b/src/from/dynamodb.js index 1d5e3212..be3c2597 100644 --- a/src/from/dynamodb.js +++ b/src/from/dynamodb.js @@ -35,7 +35,6 @@ export const fromDynamodb = (event, { type: `${calculateEventTypePrefix(record, { skFn, discriminatorFn, eventTypePrefix })}-${calculateEventTypeSuffix(record)}`, partitionKey: record.dynamodb.Keys[pkFn].S, timestamp: deriveTimestamp(record), - approximateCreationTimestamp: ddbApproximateCreationTimestamp(record), tags: { region: record.awsRegion, }, @@ -92,7 +91,7 @@ const calculateEventTypeSuffix = (record) => { const deriveTimestamp = (record) => parseInt(record.dynamodb.NewImage?.timestamp?.N, 10) || ddbApproximateCreationTimestamp(record); -const ddbApproximateCreationTimestamp = (record) => record.dynamodb.ApproximateCreationDateTime * 1000; +export const ddbApproximateCreationTimestamp = (record) => record.dynamodb.ApproximateCreationDateTime * 1000; //-------------------------------------------- // global table support - version: 2017.11.29 diff --git a/test/unit/from/dynamodb.test.js b/test/unit/from/dynamodb.test.js index be7230ad..e2c7554d 100644 --- a/test/unit/from/dynamodb.test.js +++ b/test/unit/from/dynamodb.test.js @@ -93,7 +93,6 @@ describe('from/dynamodb.js', () => { type: 'thing-created', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -203,7 +202,6 @@ describe('from/dynamodb.js', () => { type: 'thing-created', partitionKey: '1', timestamp: 1572832690001, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -340,7 +338,6 @@ describe('from/dynamodb.js', () => { type: 'thing-updated', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -447,7 +444,6 @@ describe('from/dynamodb.js', () => { type: 'thing-deleted', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -557,7 +553,6 @@ describe('from/dynamodb.js', () => { type: 'thing-deleted', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -682,7 +677,6 @@ describe('from/dynamodb.js', () => { type: 'thing-undeleted', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -781,7 +775,6 @@ describe('from/dynamodb.js', () => { type: 'thing-deleted', partitionKey: '1', timestamp: 1573005490000, - approximateCreationTimestamp: 1573005490000, tags: { region: 'us-west-2', }, @@ -959,7 +952,6 @@ describe('from/dynamodb.js', () => { type: 'thing-updated', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -1041,7 +1033,6 @@ describe('from/dynamodb.js', () => { type: 'thing-updated', partitionKey: '1', timestamp: 1572832990000, - approximateCreationTimestamp: 1572832990000, tags: { region: 'us-east-1', }, diff --git a/test/unit/utils/encryption.test.js b/test/unit/utils/encryption.test.js index 2489dfd6..b2e17b74 100644 --- a/test/unit/utils/encryption.test.js +++ b/test/unit/utils/encryption.test.js @@ -62,7 +62,6 @@ describe('utils/encryption.js', () => { type: 'thing-created', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -395,7 +394,6 @@ describe('utils/encryption.js', () => { type: 'thing-created', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -477,7 +475,6 @@ describe('utils/encryption.js', () => { type: 'thing-updated', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', }, @@ -548,7 +545,6 @@ describe('utils/encryption.js', () => { type: 'thing-deleted', partitionKey: '1', timestamp: 1572832690000, - approximateCreationTimestamp: 1572832690000, tags: { region: 'us-west-2', },