diff --git a/lib/dao.js b/lib/dao.js index 58c93efb..9e58fc4f 100644 --- a/lib/dao.js +++ b/lib/dao.js @@ -2297,6 +2297,10 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op assert(typeof connector.updateAttributes === 'function', 'updateAttributes() must be implemented by the connector'); + var allowExtendedOperators = connector.settings + && connector.settings.allowExtendedOperators; + + var strict = this.__strict; var model = Model.modelName; var hookState = {}; @@ -2334,6 +2338,24 @@ DataAccessObject.prototype.updateAttributes = function updateAttributes(data, op if (err) return cb(err); data = ctx.data; + if (strict && !allowExtendedOperators) { + var props = Model.definition.properties; + var keys = Object.keys(data); + var result = {}; + for (var i = 0; i < keys.length; i++) { + key = keys[i]; + if (props[key]) { + result[key] = data[key]; + } else if (strict === 'throw') { + cb(new Error('Unknown property: ' + key)); + return; + } else if (strict === 'validate') { + inst.__unknownProperties.push(key); + } + } + data = removeUndefined(result); + } + // update instance's properties inst.setAttributes(data); diff --git a/test/manipulation.test.js b/test/manipulation.test.js index b6efb87b..3877f44e 100644 --- a/test/manipulation.test.js +++ b/test/manipulation.test.js @@ -19,7 +19,7 @@ describe('manipulation', function () { age: {type: Number, index: true}, dob: Date, createdAt: {type: Date, default: Date} - }, { forceId: true }); + }, { forceId: true, strict: true }); db.automigrate(done); @@ -490,6 +490,54 @@ describe('manipulation', function () { }); }); }); + + it('should ignore unknown attributes when strict: true', function(done) { + person.updateAttributes({foo:'bar'}, + function(err, p) { + if (err) return done(err); + should.not.exist(p.foo); + Person.findById(p.id, function(e, p) { + if (e) return done(e); + should.not.exist(p.foo); + done(); + }); + }); + }); + + it('should throw error on unknown attributes when strict: throw', function(done) { + Person.definition.settings.strict = 'throw'; + Person.findById(person.id, function(err, p) { + p.updateAttributes({foo:'bar'}, + function(err, p) { + should.exist(err); + err.name.should.equal('Error'); + err.message.should.equal('Unknown property: foo'); + should.not.exist(p); + Person.findById(person.id, function(e, p) { + if (e) return done(e); + should.not.exist(p.foo); + done(); + }); + }); + }); + }); + + it('should throw error on unknown attributes when strict: throw', function(done) { + Person.definition.settings.strict = 'validate'; + Person.findById(person.id, function(err, p) { + p.updateAttributes({foo:'bar'}, + function(err, p) { + should.exist(err); + err.name.should.equal('ValidationError'); + err.message.should.containEql('`foo` is not defined in the model'); + Person.findById(person.id, function(e, p) { + if (e) return done(e); + should.not.exist(p.foo); + done(); + }); + }); + }); + }); it('should allow same id value on updateAttributes', function(done) { person.updateAttributes({id: person.id, name: 'John'}, @@ -532,7 +580,7 @@ describe('manipulation', function () { }); }); - it('should allows model instance on updateAttributes', function(done) { + it('should allow model instance on updateAttributes', function(done) { person.updateAttributes(new Person({'name': 'John', age: undefined}), function(err, p) { if (err) return done(err); @@ -545,7 +593,7 @@ describe('manipulation', function () { }); }); - it('should allows model instance on updateAttributes (promise variant)', function(done) { + it('should allow model instance on updateAttributes (promise variant)', function(done) { person.updateAttributes(new Person({'name': 'Jane', age: undefined})) .then(function(p) { return Person.findById(p.id)