diff --git a/package.json b/package.json index 010ab4b..efa16b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nodenamo", - "version": "1.7.4", + "version": "1.7.2", "description": "A powerful ORM for DynamoDb", "main": "dist/index.js", "typings": "dist/index", diff --git a/spec/unit/dynamodbManagerUpdate.spec.ts b/spec/unit/dynamodbManagerUpdate.spec.ts index 3765aea..a5e50f8 100644 --- a/spec/unit/dynamodbManagerUpdate.spec.ts +++ b/spec/unit/dynamodbManagerUpdate.spec.ts @@ -20,7 +20,6 @@ describe('DynamoDbManager.Update()', function () let called:boolean; let deleted:boolean; let deleted2:boolean; - let desiredObjectCreatedFromStronglyConsistentRead:boolean; beforeEach(()=> { @@ -32,11 +31,6 @@ describe('DynamoDbManager.Update()', function () called = false; deleted = false; deleted2 = false; - desiredObjectCreatedFromStronglyConsistentRead = false; - }); - - afterEach(()=>{ - desiredObjectCreatedFromStronglyConsistentRead = true; }); it('update() - no keys', async () => @@ -57,13 +51,13 @@ describe('DynamoDbManager.Update()', function () order:number; }; - setupStronglyConsistentRead({hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}); let findResponse = getMockedQueryResponse({Items: [{hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}, {hash: 'entity#1', range: 'order', id:1, name:'Some Two', created:'created', order:'order'}]}); mockedClient.setup(q => q.query(It.is(p => !!p.TableName && p.IndexName === Const.IdIndexName && p.KeyConditionExpression === '#objid = :objid' && p.ExpressionAttributeNames['#objid'] === Const.IdColumn && p.ExpressionAttributeValues[':objid'] === 'entity#1'))).callback(()=>called=true).returns(()=>findResponse.object); let manager = new DynamoDbManager(mockedClient.object); await manager.update(Entity, 1, {name: 'New Two'}, undefined, mockedTransaction.object); + assert.isTrue(called); }); @@ -85,7 +79,6 @@ describe('DynamoDbManager.Update()', function () order:number; }; - setupStronglyConsistentRead({hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}); let findResponse = getMockedQueryResponse({Items: [{hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}, {hash: 'entity#1', range: 'order', id:1, name:'Some Two', created:'created', order:'order'}]}); mockedClient.setup(q => q.query(It.is(p => !!p.TableName && p.IndexName === Const.IdIndexName && p.KeyConditionExpression === '#objid = :objid' && p.ExpressionAttributeNames['#objid'] === Const.IdColumn && p.ExpressionAttributeValues[':objid'] === 'entity#1'))).callback(()=>called=true).returns(()=>findResponse.object); mockedTransaction.setup(t => t.add(It.is(t => !!t.Put && !!t.Put.TableName && t.Put.Item.hash === 'entity#1' && t.Put.Item.range === 'new created' && t.Put.Item.name === 'New Two' && t.Put.Item.created === 'new created' && t.Put.Item.order === 'new order'))).callback(()=>put=true); @@ -118,7 +111,6 @@ describe('DynamoDbManager.Update()', function () created:number; }; - setupStronglyConsistentRead({hash: 'entity#nodenamo', range: 'created#1', id:1, name:'Some One', created:'created', order:'order'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'entity#nodenamo', range: 'created#1', id:1, name:'Some One', created:'created', order:'order'}, {hash: 'entity#Some One', range: 'created', id:1, name:'Some One', created:'created', order:'order'}, @@ -158,7 +150,6 @@ describe('DynamoDbManager.Update()', function () order:number; }; - setupStronglyConsistentRead({hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}); let findResponse = getMockedQueryResponse({Items: [{hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}, {hash: 'entity#1', range: 'order', id:1, name:'Some Two', created:'created', order:'order'}]}); mockedClient.setup(q => q.query(It.is(p => !!p.TableName && p.IndexName === Const.IdIndexName && p.KeyConditionExpression === '#objid = :objid' && p.ExpressionAttributeNames['#objid'] === Const.IdColumn && p.ExpressionAttributeValues[':objid'] === 'entity#1'))).callback(()=>called=true).returns(()=>findResponse.object); mockedTransaction.setup(t => t.add(It.is(t => !!t.Put && !!t.Put.TableName && t.Put.Item.hash === 'entity#1' && t.Put.Item.range === 'created' && !t.Put.ConditionExpression && t.Put.Item.name === 'New Two' && t.Put.Item.created === 'created' && t.Put.Item.order === 'order'))).callback(()=>put=true); @@ -174,43 +165,6 @@ describe('DynamoDbManager.Update()', function () assert.isFalse(deleted); }); - it('update() - delta change, change from strongly consistent read reflected in transaction - when missing from getOneRows', async () => - { - @DBTable() - class Entity - { - @DBColumn({hash:true}) - id:number; - - @DBColumn() - name:string; - - @DBColumn({range:true}) - created:number; - - @DBColumn({range:true}) - order:number; - - @DBColumn() - recentlyUpdatedField:number; - }; - - setupStronglyConsistentRead({hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order', recentlyUpdatedField:1}); //recentlyUpdatedField exists in strongly consistent read - let findResponse = getMockedQueryResponse({Items: [{hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}, {hash: 'entity#1', range: 'order', id:1, name:'Some Two', created:'created', order:'order'}]});//recentlyUpdatedField does NOT exist in eventually consistent read - mockedClient.setup(q => q.query(It.is(p => !!p.TableName && p.IndexName === Const.IdIndexName && p.KeyConditionExpression === '#objid = :objid' && p.ExpressionAttributeNames['#objid'] === Const.IdColumn && p.ExpressionAttributeValues[':objid'] === 'entity#1'))).callback(()=>called=true).returns(()=>findResponse.object); - mockedTransaction.setup(t => t.add(It.is(t => !!t.Put && !!t.Put.TableName && t.Put.Item.hash === 'entity#1' && t.Put.Item.range === 'created' && !t.Put.ConditionExpression && t.Put.Item.name === 'New Two' && t.Put.Item.created === 'created' && t.Put.Item.order === 'order' && t.Put.Item.recentlyUpdatedField === 1))).callback(()=>put=true); //recentlyUpdatedField included in update payload - mockedTransaction.setup(t => t.add(It.is(t => !!t.Put && !!t.Put.TableName && t.Put.Item.hash === 'entity#1' && t.Put.Item.range === 'order' && !t.Put.ConditionExpression && t.Put.Item.name === 'New Two' && t.Put.Item.created === 'created' && t.Put.Item.order === 'order' && t.Put.Item.recentlyUpdatedField === 1))).callback(()=>put2=true); - mockedTransaction.setup(t => t.add(It.is(t => !!t.Delete))).callback(()=>deleted=true); - - let manager = new DynamoDbManager(mockedClient.object); - await manager.update(Entity, 1, {name: 'New Two'}, undefined, mockedTransaction.object); - - assert.isTrue(called); - assert.isTrue(put); - assert.isTrue(put2); - assert.isFalse(deleted); - }); - it('update() - key changed', async () => { @DBTable() @@ -229,7 +183,6 @@ describe('DynamoDbManager.Update()', function () order:number; }; - setupStronglyConsistentRead({hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}); let findResponse = getMockedQueryResponse({Items: [{hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}, {hash: 'entity#1', range: 'order', id:1, name:'Some Two', created:'created', order:'order'}]}); mockedClient.setup(q => q.query(It.is(p => !!p.TableName && p.IndexName === Const.IdIndexName && p.KeyConditionExpression === '#objid = :objid' && p.ExpressionAttributeNames['#objid'] === Const.IdColumn && p.ExpressionAttributeValues[':objid'] === 'entity#1'))).callback(()=>called=true).returns(()=>findResponse.object); mockedTransaction.setup(t => t.add(It.is(t => !!t.Put && !!t.Put.TableName && t.Put.Item.hash === 'entity#1a' && t.Put.Item.range === 'created' && !!t.Put.ConditionExpression && t.Put.Item.id === '1a'))).callback(()=>put=true); @@ -267,7 +220,6 @@ describe('DynamoDbManager.Update()', function () let condition = {conditionExpression: 'condition', expressionAttributeNames: {'#n': 'name'}, expressionAttributeValues: {':v': true}}; - setupStronglyConsistentRead({hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}); let findResponse = getMockedQueryResponse({Items: [{hash: 'entity#1', range: 'created', id:1, name:'Some One', created:'created', order:'order'}, {hash: 'entity#1', range: 'order', id:1, name:'Some Two', created:'created', order:'order'}]}); mockedClient.setup(q => q.query(It.is(p => !!p.TableName && p.IndexName === Const.IdIndexName && p.KeyConditionExpression === '#objid = :objid' && p.ExpressionAttributeNames['#objid'] === Const.IdColumn && p.ExpressionAttributeValues[':objid'] === 'entity#1'))).callback(()=>called=true).returns(()=>findResponse.object); mockedTransaction.setup(t => t.add(It.is(t => !!t.Put && !!t.Put.TableName && t.Put.Item.hash === 'entity#1' && t.Put.Item.range === 'created' && t.Put.Item.name === 'New Two' && t.Put.ConditionExpression === 'condition' && t.Put.ExpressionAttributeNames['#n'] === 'name' && t.Put.ExpressionAttributeValues[':v'] === 'true'))).callback(()=>put=true); @@ -295,7 +247,6 @@ describe('DynamoDbManager.Update()', function () name:string; }; - setupStronglyConsistentRead({hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}, ]}); @@ -327,7 +278,6 @@ describe('DynamoDbManager.Update()', function () name:string; }; - setupStronglyConsistentRead({hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}, ]}); @@ -359,7 +309,6 @@ describe('DynamoDbManager.Update()', function () name:string; }; - setupStronglyConsistentRead({hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}, ]}); @@ -391,7 +340,6 @@ describe('DynamoDbManager.Update()', function () name:string; }; - setupStronglyConsistentRead({hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}, ]}); @@ -423,7 +371,6 @@ describe('DynamoDbManager.Update()', function () name:string; }; - setupStronglyConsistentRead({hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}, ]}); @@ -515,7 +462,6 @@ describe('DynamoDbManager.Update()', function () name:string; }; - setupStronglyConsistentRead({id:1, name:'Some One'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}, ]}); @@ -556,7 +502,6 @@ describe('DynamoDbManager.Update()', function () @DBColumn() name:string; }; - setupStronglyConsistentRead({hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}, @@ -593,7 +538,6 @@ describe('DynamoDbManager.Update()', function () @DBColumn() name:string; }; - setupStronglyConsistentRead({hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}); let findResponse = getMockedQueryResponse({Items: [ {hash: 'hash', range: 'range', id:1, objver: 1, name:'Some One'}, @@ -617,20 +561,6 @@ describe('DynamoDbManager.Update()', function () assert.isDefined(error); assert.isTrue(error.message.includes('An object with the same ID or hash-range key already exists in \'Entity\' table')); }); - - function setupStronglyConsistentRead(expectedItem:object) - { - let stronglyConsistentResponse = getMockedGetResponse( - {Item:expectedItem} - ); - mockedClient.setup(q => q.get(It.is(p => - !!p.TableName - && p.Key[Const.HashColumn] === 'entity#1' - && p.Key[Const.RangeColumn] === 'nodenamo' - && p.ConsistentRead === true))) - .callback(()=>desiredObjectCreatedFromStronglyConsistentRead=true) - .returns(()=>stronglyConsistentResponse.object); - } }); function getMockedGetResponse(response:GetItemOutput): IMock> diff --git a/src/managers/dynamodbManager.ts b/src/managers/dynamodbManager.ts index 8c3fb86..2fa0abe 100644 --- a/src/managers/dynamodbManager.ts +++ b/src/managers/dynamodbManager.ts @@ -105,8 +105,10 @@ export class DynamoDbManager implements IDynamoDbManager [Const.RangeColumn]: attributeValues[':range'] }, ConsistentRead: stronglyConsistent - }; + }; + let response = await this.client.get(query).promise(); + if(response.Item) { return EntityFactory.create(type, response.Item); @@ -254,7 +256,7 @@ export class DynamoDbManager implements IDynamoDbManager let versioningRequired = tableVersioning || (params && params.versionCheck); //Calculate new representations - let [rows, idRow] = await Promise.all([this.getById(id, type),this.getOne(type,id,{stronglyConsistent:true})]); + let rows = await this.getById(id, type); if(rows.length === 0) { @@ -282,8 +284,8 @@ export class DynamoDbManager implements IDynamoDbManager //Must assign data to `instance` so it has @DBColumn() and @DBTable() metadata. //EntityFactory is used here to convert an object with custom column names to an object with real property names/ //And because of an object created by EntityFactory.create() will not have Const.VersionColumn property, we have to re-add it here as well. - let desiredObject = Object.assign(instance, Object.assign(Object.assign({}, EntityFactory.create(type, idRow)), noUndefinedValuesObject)); - desiredObject[Const.VersionColumn] = idRow[Const.VersionColumn]; + let desiredObject = Object.assign(instance, Object.assign(Object.assign({}, EntityFactory.create(type, rows[0])), noUndefinedValuesObject)); + desiredObject[Const.VersionColumn] = rows[0][Const.VersionColumn]; //If versionCheck, use the version from the original object. //Else, RepresentationFactory.get() will increment the version from DB. @@ -381,6 +383,7 @@ export class DynamoDbManager implements IDynamoDbManager } if(!autoCommit) return; + try { await transaction.commit(); @@ -393,7 +396,7 @@ export class DynamoDbManager implements IDynamoDbManager let currentObject:T; try { - currentObject = await this.getOne(type, id, {stronglyConsistent:true}); + currentObject = await this.getOne(type, id); } catch(e2) {