From fc86a0984a34646e6675955915cb1a1528ecda36 Mon Sep 17 00:00:00 2001 From: Eduardo Ramirez Date: Wed, 28 Feb 2018 12:02:35 -0800 Subject: [PATCH] FakeDynamo accounts for GSIs with no range keys --- lib/FakeDynamo.js | 19 ++++++++++++------- test/testFakeDynamo.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/lib/FakeDynamo.js b/lib/FakeDynamo.js index 0fbaa5e..adeaa01 100644 --- a/lib/FakeDynamo.js +++ b/lib/FakeDynamo.js @@ -150,7 +150,7 @@ FakeTable.prototype.setRangeKey = function(name, type) { /** * Set GSI definitions for this table. * - * @param {Array.<{hash:({name:(string), type:(string)}), range:({name:(string), type:(string)})}>} gsiDefinitions + * @param {Array.<{hash:({name:(string), type:(string)}), ?range:({name:(string), type:(string)})}>} gsiDefinitions * @return {FakeTable} the current FakeTable instance */ FakeTable.prototype.setGsiDefinitions = function(gsiDefinitions) { @@ -240,8 +240,11 @@ FakeTable.prototype.query = function(data) { FakeTable.prototype._queryGlobalSecondaryIndex = function(data) { var indexParts = data.IndexName.split('-') var gsis = this.gsiDefinitions.filter(function (gsi) { - return indexParts.indexOf(gsi.hash.name) >= 0 && - indexParts.indexOf(gsi.range.name) >= 0 + if (gsi.range) { + return indexParts.indexOf(gsi.hash.name) >= 0 && + indexParts.indexOf(gsi.range.name) >= 0 + } + return indexParts.indexOf(gsi.hash.name) >= 0 }) if (gsis.length == 0) { @@ -249,7 +252,9 @@ FakeTable.prototype._queryGlobalSecondaryIndex = function(data) { } // We should only find 1 gsi, so use the first one - this._validateFilterKeys(data, gsis[0].hash.name, gsis[0].range.name) + var hashKey = gsis[0].hash.name + var rangeKey = gsis[0].range && gsis[0].range.name + this._validateFilterKeys(data, hashKey, rangeKey) // store keys, values, and conditions from data for easy access in scan var keyConditionFn = getKeyConditionFn(data) @@ -542,11 +547,11 @@ FakeTable.prototype._validateKeySize = function (key) { * * @param {Object} data * @param {string} partitionKey - * @param {string} sortKey + * @param {?string} opt_sortKey */ -FakeTable.prototype._validateFilterKeys = function (data, partitionKey, sortKey) { +FakeTable.prototype._validateFilterKeys = function (data, partitionKey, opt_sortKey) { forEachFilterCondition(data, function (condition, key) { - if (key === partitionKey || key === sortKey) { + if (key === partitionKey || key === opt_sortKey) { throw new DynamoError( 'ValidationException', 'Filter Expression can only contain non-primary key attributes: Primary key attribute: ' + key diff --git a/test/testFakeDynamo.js b/test/testFakeDynamo.js index a0e07b2..81019ff 100644 --- a/test/testFakeDynamo.js +++ b/test/testFakeDynamo.js @@ -793,6 +793,41 @@ builder.add(function testQueryFilterOnGsiIndexThrowsError(test) { }) }) +builder.add(function testQueryFilterOnGsiWithNoRangeWorks(test) { + db.getTable('cookie').setGsiDefinitions([ + { + hash: { + name: 'cookieType', + type: 'S' + } + } + ]) + + db.getTable('cookie').setData({ + 'cookieA': { + 1: {cookieId: 'cookieA', column: '1', createdAt: 1, orderedAt: 27, cookieType: 'Oreo'}, + 2: {cookieId: 'cookieA', column: '2', createdAt: 2, orderedAt: 29, cookieType: 'Oreo'} + }, + 'cookieB': { + 1: {cookieId: 'cookieB', column: '1', createdAt: 2, orderedAt: 12, cookieType: 'Snickerdoodle'}, + 2: {cookieId: 'cookieB', column: '2', createdAt: 1, orderedAt: 14, cookieType: 'Snickerdoodle'} + } + }) + + var filter = client.newConditionBuilder() + .filterAttributeLessThan('orderedAt', 28) + + return client.newQueryBuilder('cookie') + .setHashKey('cookieType', 'Oreo') + .setIndexName('index-cookieType-gsi') + .withFilter(filter) + .execute() + .then(function (data) { + test.equal(data.result.length, 1) + test.deepEqual(data.result[0], {cookieId: 'cookieA', column: '1', createdAt: 1, orderedAt: 27, cookieType: 'Oreo'}) + }) +}) + builder.add(function testQueryFilterWithRangeWithIndexDoesNotThrowsError(test) { var filter = client.newConditionBuilder() .filterAttributeLessThan('createdAt', 30)