Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.nyc_output
.nyc_output/
coverage/
node_modules/
13 changes: 13 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,19 @@ Update a single record. Passthrough to [DocumentClient.update](http://docs.aws.a

Returns **Request**

## dynamicUpdate

Updates a single record, dynamically generating parts of the update statement from an object.
Given a new object to update within a table, the necessary parameters to update every key in this new object will be generated.

**Parameters**

- `newObject` **object** all of this object's properties will be used to update the row in the table
- `params` **object** standard update request parameters. See [DocumentClient.update](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#update-property) for details. ExpressionAttributeNames, ExpressionAttributeValues, and UpdateExpression fields not reqiured
- `callback` **[function]** a function to handle the response. See [DocumentClient.update](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#update-property) for details.

Returns **Request**

# CompleteRequestSet

An array of [AWS.Requests](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/Request.html)
Expand Down
14 changes: 13 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,18 @@ function Dyno(options) {
* @param {function} [callback] - a function to handle the response. See [DocumentClient.scan](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#scan-property) for details.
* @returns a Request if not paginating, or a ReadableStream if multiple pages were requested
*/
scan: require('./lib/paginated')(docClient).scan
scan: require('./lib/paginated')(docClient).scan,
/**
* Given a object, generates the necessary parameters to update every key in the new object within a given Dynamo table and executes the update
*
* @instanceof
* @memberof client
* @param {object} newObject - all of this object's properties will be used to update the row in the table
* @param {object} updateParams - standard parameters object for a [DocumentClient.update](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#update-property) call, ExpressionAttributeNames, ExpressionAttributeValues, and UpdateExpression not reqiured
* @param {function} callback - a function to handle the response. See [DocumentClient.update](http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html#update-property) for details.
* @returns {Request}
*/
dynamicUpdate: require('./lib/update')(docClient).dynamicUpdate
};

// Drop specific functions from read/write only clients
Expand All @@ -350,6 +361,7 @@ function Dyno(options) {
delete dynoExtensions.batchWriteItemRequests;
delete dynoExtensions.batchWriteAll;
delete dynoExtensions.putStream;
delete dynoExtensions.dynamicUpdate;
}

if (options.write) {
Expand Down
27 changes: 27 additions & 0 deletions lib/update.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
var _ = require('underscore');

module.exports = function(client) {
var updates = {};

updates.dynamicUpdate = function (newObject, updateParams, callback) {
var expressionObject = {
ExpressionAttributeNames: {},
ExpressionAttributeValues: {}
};
var updateExpressionParts = [];

Object.keys(newObject).forEach(function (key) {
if (Object.keys(updateParams.Key).indexOf(key) !== -1) return;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I suggest we rename key to something else? Right now we're checking whether key is in the updateParams.Key's key list, it might be clearer if it was more explicit, i.e. newObjectKey or similar.


expressionObject.ExpressionAttributeNames['#' + key] = key;
expressionObject.ExpressionAttributeValues[':' + key] = newObject[key];
updateExpressionParts.push('#' + key + ' = :' + key);
});
expressionObject.UpdateExpression = 'set ' + updateExpressionParts.join(', ');

updateParams = _.extend(updateParams, expressionObject);
return client.update(updateParams, callback);
};

return updates;
};
4 changes: 4 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ test('[index] expected properties', function(assert) {
assert.equal(typeof dyno.query, 'function', 'exposes query function');
assert.equal(typeof dyno.scan, 'function', 'exposes scan function');
assert.equal(typeof dyno.updateItem, 'function', 'exposes updateItem function');
assert.equal(typeof dyno.dynamicUpdate, 'function', 'exposes dynamicUpdate function');
assert.equal(typeof dyno.batchGetItemRequests, 'function', 'exposes batchGetItemRequests function');
assert.equal(typeof dyno.batchWriteItemRequests, 'function', 'exposes batchWriteItemRequests function');
assert.equal(typeof dyno.batchGetAll, 'function', 'exposes batchGetAll function');
Expand All @@ -50,6 +51,7 @@ test('[index] expected properties', function(assert) {
assert.equal(typeof read.query, 'function', 'read-only client exposes query function');
assert.equal(typeof read.scan, 'function', 'read-only client exposes scan function');
assert.equal(typeof read.updateItem, 'undefined', 'read-only client does not expose updateItem function');
assert.equal(typeof read.dynamicUpdate, 'undefined', 'read-only client does not expose dynamicUpdate function');
assert.equal(typeof read.batchGetItemRequests, 'function', 'read-only client exposes batchGetItemRequests function');
assert.equal(typeof read.batchWriteItemRequests, 'undefined', 'read-only client does not expose batchWriteItemRequests function');
assert.equal(typeof read.batchGetAll, 'function', 'read-only client exposes batchGetAll function');
Expand All @@ -73,6 +75,7 @@ test('[index] expected properties', function(assert) {
assert.equal(typeof write.query, 'undefined', 'write-only client does not expose query function');
assert.equal(typeof write.scan, 'undefined', 'write-only client does not expose scan function');
assert.equal(typeof write.updateItem, 'function', 'write-only client exposes updateItem function');
assert.equal(typeof write.dynamicUpdate, 'function', 'write-only client exposes dynamicUpdate function');
assert.equal(typeof write.batchGetItemRequests, 'undefined', 'write-only client does not expose batchGetItemRequests function');
assert.equal(typeof write.batchWriteItemRequests, 'function', 'write-only client exposes batchWriteItemRequests function');
assert.equal(typeof write.batchGetAll, 'undefined', 'write-only client does not expose batchGetAll function');
Expand All @@ -99,6 +102,7 @@ test('[index] expected properties', function(assert) {
assert.equal(typeof multi.query, 'function', 'multi-client exposes query function');
assert.equal(typeof multi.scan, 'function', 'multi-client exposes scan function');
assert.equal(typeof multi.updateItem, 'function', 'multi-client exposes updateItem function');
assert.equal(typeof multi.dynamicUpdate, 'function', 'multi-client exposes dynamicUpdate function');
assert.equal(typeof multi.batchGetItemRequests, 'function', 'exposes batchGetItemRequests function');
assert.equal(typeof multi.batchWriteItemRequests, 'function', 'exposes batchWriteItemRequests function');
assert.equal(typeof multi.batchGetAll, 'function', 'exposes batchGetAll function');
Expand Down
79 changes: 79 additions & 0 deletions test/update.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
var test = require('tape');
var testTables = require('./test-tables');
var dynamodb = require('dynamodb-test')(test, 'dyno', testTables.idhash);
var Dyno = require('..');
var Update = require('../lib/update');
var _ = require('underscore');

var fixtures = _.range(10).map(function (i) {
return {
id: i.toString(),
idPower2: Math.pow(i, 2),
text: 'string'
};
});

test('[update] properties', function (assert) {
var update = Update(dynamodb.dynamodb);
assert.equal(typeof update.dynamicUpdate, 'function', 'exposes dynamicUpdate function');
assert.end();
});

dynamodb.start();

dynamodb.test('[update] dynamicUpdate - update all fields', fixtures, function (assert) {
var dyno = Dyno({
table: dynamodb.tableName,
region: 'local',
endpoint: 'http://localhost:4567'
});

var newObject = {
id: '2',
idPower2: -99,
text: 'update string'
};
var updateParams = {
TableName: dynamodb.tableName,
Key: { id: '2' },
ReturnValues: 'ALL_NEW'
};

dyno.dynamicUpdate(newObject, updateParams, function (err, response) {
assert.ifError(err, 'dynamicUpdate errored');
assert.deepEqual(response, { Attributes: { id: '2', idPower2: -99, text: 'update string' } }, 'expected new values');
assert.end();
});
});

dynamodb.test('[update] dynamicUpdate - only add new field', fixtures, function (assert) {
var dyno = Dyno({
table: dynamodb.tableName,
region: 'local',
endpoint: 'http://localhost:4567'
});

var newObject = {
id: '2',
newDateField: '2017-10-31'
};
var updateParams = {
TableName: dynamodb.tableName,
Key: { id: '0' },
ReturnValues: 'ALL_NEW'
};

var expectedRow = { id: '0', idPower2: 0, newDateField: '2017-10-31', text: 'string' };
dyno.dynamicUpdate(newObject, updateParams, function (err, response) {
assert.ifError(err, 'dynamicUpdate errored');
assert.deepEqual(response, { Attributes: expectedRow }, 'expected new values');
dyno.query({ KeyConditionExpression: 'id = :id', ExpressionAttributeValues: { ':id': '0' } }, function (err, queryResponse) {
assert.ifError(err, 'query error');
assert.deepEqual(queryResponse.Items, [expectedRow], 'expected query response');
assert.end();
});
});
});

dynamodb.delete();
dynamodb.close();