Skip to content

Commit

Permalink
fix: stop conditions being overwritten
Browse files Browse the repository at this point in the history
Condition values referencing timestamps could be overwritten by both
when either updateTimestamps flag was on or by the set method. This
would cause conditions to fail.

The change disables these modification paths in `Attribute.toDynamo` for
condition values.
  • Loading branch information
tgandrews committed Feb 18, 2019
1 parent 41ce09f commit 966d7bc
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 6 deletions.
12 changes: 6 additions & 6 deletions lib/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function Model (obj) {
}
}

async function processCondition (req, options, schema) {
async function processCondition (req, options, model) {
if (options.condition) {
if (req.ConditionExpression) {
req.ConditionExpression = `(${req.ConditionExpression}) and (${options.condition})`;
Expand All @@ -45,9 +45,9 @@ async function processCondition (req, options, schema) {
const k = keys[i];

const val = options.conditionValues[k];
const attr = schema.attributes[k];
const attr = model.$__.schema.attributes[k];
if (attr) {
req.ExpressionAttributeValues[`:${k}`] = await attr.toDynamo(val);
req.ExpressionAttributeValues[`:${k}`] = await attr.toDynamo(val, undefined, model, {'updateTimestamps': false});
} else {
throw new errors.ModelError(`Invalid condition value: ${k}. The name must either be in the schema or a full DynamoDB object must be specified.`);
}
Expand Down Expand Up @@ -419,7 +419,7 @@ Model.conditionCheck = async function (NewModel, key, options, next) {
const rangeKeyName = schema.rangeKey.name;
conditionReq.Key[rangeKeyName] = schema.rangeKey.toDynamo(key[rangeKeyName], undefined, key);
}
await processCondition(conditionReq, options, schema);
await processCondition(conditionReq, options, NewModel);

debug('Condition Check', conditionReq);
deferred.resolve(conditionReq);
Expand Down Expand Up @@ -488,7 +488,7 @@ Model.prototype.put = async function (options, next) {
item.ExpressionAttributeNames['#__hash_key'] = schema.hashKey.name;
}
}
await processCondition(item, options, schema);
await processCondition(item, options, this);

debug('putItem', item);

Expand Down Expand Up @@ -776,7 +776,7 @@ Model.update = async function (NewModel, key, update, options, next) {
'ExpressionAttributeValues': {},
'ReturnValues': options.returnValues
};
await processCondition(updateReq, options, NewModel.$__.schema);
await processCondition(updateReq, options, NewModel);

updateReq.Key[hashKeyName] = await schema.hashKey.toDynamo(key[hashKeyName], undefined, key);

Expand Down
28 changes: 28 additions & 0 deletions test/Model.js
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,34 @@ describe('Model', function () {
});
});

it('should save without updating timestamps in conditions', (done) => {
const myCat = new Cats.Cat9({
'id': 1,
'name': 'Fluffy',
'vet': {'name': 'theVet', 'address': '12 somewhere'},
'ears': [{'name': 'left'}, {'name': 'right'}],
'legs': ['front right', 'front left', 'back right', 'back left'],
'more': {'favorites': {'food': 'fish'}},
'array': [{'one': '1'}],
'validated': 'valid'
});

myCat.save((err, theSavedCat1) => {
const savedUpdatedAt = theSavedCat1.updatedAt;

myCat.name = 'FluffyB';
myCat.save({
'condition': 'updatedAt = :updatedAt',
'conditionValues': {
'updatedAt': savedUpdatedAt
}
}, (error) => {
should(error).eql(null);
done();
});
});
});


it('Save existing item with updating expires', (done) => {
const myCat = new Cats.Cat11({
Expand Down

0 comments on commit 966d7bc

Please # to comment.