不要怂,就是干,撸起袖子干!

Commit d5607665 by Jan Aagaard Meier

change(validation): Remove hookValidate, and add hooks param to validate. Closes…

… # 3439. Update changelog for v4 pre release
1 parent 7a1286d1
# Future (4.0.0-1)
- [CHANGED] Remove `hookValidate` in favor of `validate` with `hooks: true | false`.
- [REMOVED] Support for `referencesKey`
- [CHANGED] Throw if `dialect` is not provided to the constructor
## BC breaks:
- `hookValidate` removed in favor of `validate` with `hooks: true | false`. `validate` returns a promise which is rejected if validation fails
- Removed support for `referencesKey`, use a `references` object
- Remove default dialect
# 3.23.2
- [FIXED] Type validation now works with non-strings due to updated validator@5.0.0 [#5861](https://github.com/sequelize/sequelize/pull/5861)
- [FIXED] Improved offset and limit support for SQL server 2008 [#5616](https://github.com/sequelize/sequelize/pull/5616)
......
......@@ -57,7 +57,7 @@ InstanceValidator.RAW_KEY_NAME = '__raw';
*
* @return {Promise}
*/
InstanceValidator.prototype.validate = function() {
InstanceValidator.prototype._validate = function() {
if (this.inProgress) {
throw new Error('Validations already in progress.');
}
......@@ -71,9 +71,7 @@ InstanceValidator.prototype.validate = function() {
return promise.reflect();
})).then(function() {
if (self.errors.length) {
return new sequelizeError.ValidationError(null, self.errors);
} else {
return null;
throw new sequelizeError.ValidationError(null, self.errors);
}
});
};
......@@ -87,19 +85,20 @@ InstanceValidator.prototype.validate = function() {
*
* @return {Promise}
*/
InstanceValidator.prototype.hookValidate = function() {
var self = this;
return self.modelInstance.Model.runHooks('beforeValidate', self.modelInstance, self.options).then(function() {
return self.validate().then(function(error) {
if (error) {
return self.modelInstance.Model.runHooks('validationFailed', self.modelInstance, self.options, error).then(function(newError) {
InstanceValidator.prototype.validate = function() {
if (this.options.hooks) {
return this.modelInstance.Model.runHooks('beforeValidate', this.modelInstance, this.options).bind(this).then(function() {
return this._validate().bind(this).catch(function(error) {
return this.modelInstance.Model.runHooks('validationFailed', this.modelInstance, this.options, error).then(function(newError) {
throw newError || error;
});
}
});
}).then(function() {
return self.modelInstance.Model.runHooks('afterValidate', self.modelInstance, self.options);
}).return(self.modelInstance);
return this.modelInstance.Model.runHooks('afterValidate', this.modelInstance, this.options);
}).return(this.modelInstance);
}
return this._validate();
};
/**
......
......@@ -484,6 +484,7 @@ Instance.prototype._setInclude = function(key, value, options) {
* @param {string[]} [options.fields] An optional array of strings, representing database columns. If fields is provided, only those columns will be validated and saved.
* @param {Boolean} [options.silent=false] If true, the updatedAt timestamp will not be updated.
* @param {Boolean} [options.validate=true] If false, validations won't be run.
* @param {Boolean} [options.hooks=true] Run before and after create / update + validate hooks
* @param {Function} [options.logging=false] A function that gets executed while running the query to log the sql.
* @param {Transaction} [options.transaction]
* @param {String} [options.searchPath=DEFAULT] An optional parameter to specify the schema search_path (Postgres only)
......@@ -567,14 +568,7 @@ Instance.prototype.save = function(options) {
return Promise.bind(this).then(function() {
// Validate
if (options.validate) {
return Promise.bind(this).then(function () {
// hookValidate rejects with errors, validate returns with errors
if (options.hooks) return this.hookValidate(options);
return this.validate(options).then(function (err) {
if (err) throw err;
});
});
return this.validate(options);
}
}).then(function() {
return Promise.bind(this).then(function() {
......@@ -608,14 +602,7 @@ Instance.prototype.save = function(options) {
// Validate again
options.skip = _.difference(Object.keys(this.Model.rawAttributes), hookChanged);
return Promise.bind(this).then(function () {
// hookValidate rejects with errors, validate returns with errors
if (options.hooks) return this.hookValidate(options);
return this.validate(options).then(function (err) {
if (err) throw err;
});
}).then(function() {
return this.validate(options).then(function() {
delete options.skip;
});
}
......@@ -782,6 +769,7 @@ Instance.prototype.reload = function(options) {
*
* @param {Object} [options] Options that are passed to the validator
* @param {Array} [options.skip] An array of strings. All properties that are in this array will not be validated
* @param {Boolean} [options.hooks=true] Run before and after validate hooks
* @see {InstanceValidator}
*
* @return {Promise<undefined|Errors.ValidationError>}
......@@ -790,10 +778,6 @@ Instance.prototype.validate = function(options) {
return new InstanceValidator(this, options).validate();
};
Instance.prototype.hookValidate = function(options) {
return new InstanceValidator(this, options).hookValidate();
};
/**
* This is the same as calling `set` and then calling `save` but it only saves the
* exact values passed to it, making it more atomic and safer.
......
......@@ -2063,7 +2063,7 @@ Model.prototype.upsert = function (values, options) {
, hadPrimary = this.primaryKeyField in values || this.primaryKeyAttribute in values
, instance = this.build(values);
return instance.hookValidate(options).bind(this).then(function () {
return instance.validate(options).bind(this).then(function () {
// Map field names
var updatedDataValues = _.pick(instance.dataValues, Object.keys(instance._changed))
, insertValues = Utils.mapValueFieldNames(instance.dataValues, options.fields, this)
......@@ -2162,22 +2162,14 @@ Model.prototype.bulkCreate = function(records, options) {
}).then(function() {
// Validate
if (options.validate) {
var errors = [];
var errors = []
, validateOptions = _.clone(options);
validateOptions.hooks = options.individualHooks;
return Promise.map(instances, function(instance) {
// hookValidate rejects with errors, validate returns with errors
if (options.individualHooks) {
return instance.hookValidate(options).catch(function (err) {
if (err) {
errors.push({record: instance, errors: err});
}
});
} else {
return instance.validate(options).then(function (err) {
if (err) {
return instance.validate(validateOptions).catch(function (err) {
errors.push({record: instance, errors: err});
}
});
}
}).then(function() {
delete options.skip;
if (errors.length) {
......@@ -2519,7 +2511,7 @@ Model.prototype.update = function(values, options) {
// We want to skip validations for all other fields
options.skip = Utils._.difference(Object.keys(self.attributes), Object.keys(values));
return build.hookValidate(options).then(function(attributes) {
return build.validate(options).then(function(attributes) {
options.skip = undefined;
if (attributes && attributes.dataValues) {
values = Utils._.pick(attributes.dataValues, Object.keys(values));
......
{
"name": "sequelize",
"description": "Multi dialect ORM for Node.JS/io.js",
"version": "3.23.2",
"version": "4.0.0-1",
"author": "Sascha Depold <sascha@depold.com>",
"contributors": [
{
......
......@@ -287,8 +287,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
it('should emit an error when we try to enter in a string for an auto increment key through .build().validate()', function() {
var user = this.User.build({id: 'helloworld'});
return user.validate().then(function(err) {
expect(err).to.be.an.instanceOf(Error);
return expect(user.validate()).to.be.rejected.then(function(err) {
expect(err.get('id')[0].message).to.equal('ID must be an integer!');
});
});
......@@ -367,14 +366,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
var failingUser = User.build({ name: '3' });
return failingUser.validate().then(function(error) {
return expect(failingUser.validate()).to.be.rejected.then(function(error) {
expect(error).to.be.an.instanceOf(Error);
expect(error.get('name')[0].message).to.equal("name should equal '2'");
var successfulUser = User.build({ name: '2' });
return successfulUser.validate().then(function(err) {
expect(err).to.be.null;
});
return expect(successfulUser.validate()).not.to.be.rejected;
});
});
......@@ -397,13 +394,11 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
});
return User.sync().then(function() {
return User.build({ name: 'error' }).validate().then(function(error) {
expect(error).to.be.an.instanceOf(self.sequelize.ValidationError);
return expect(User.build({ name: 'error' }).validate()).to.be.rejected.then(function(error) {
expect(error).to.be.instanceof(self.sequelize.ValidationError);
expect(error.get('name')[0].message).to.equal('Invalid username');
return User.build({ name: 'no error' }).validate().then(function(errors) {
expect(errors).to.be.null;
});
return expect(User.build({ name: 'no error' }).validate()).not.to.be.rejected;
});
});
});
......@@ -419,19 +414,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User
return expect(User
.build({ age: -1 })
.validate()
.validate())
.to.be.rejected
.then(function(error) {
expect(error).not.to.be.null;
expect(error).to.be.an.instanceOf(Error);
expect(error.get('age')[0].message).to.equal('must be positive');
// TODO: This does not test anything
// Check what the original intention was
return User.build({ age: null }).validate().then(function() {
return User.build({ age: 1 }).validate();
});
});
});
......@@ -455,19 +443,17 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return Foo
return expect(Foo
.build({ field1: null, field2: null })
.validate()
.validate())
.to.be.rejected
.then(function(error) {
expect(error).not.to.be.null;
expect(error).to.be.an.instanceOf(Error);
expect(error.get('xnor')[0].message).to.equal('xnor failed');
return Foo
return expect(Foo
.build({ field1: 33, field2: null })
.validate()
.then(function(errors) {
expect(errors).not.exist;
});
.validate())
.not.to.be.rejected;
});
});
......@@ -482,11 +468,8 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}), foo;
foo = Foo.build({bar: 'a'});
return foo.validate().then(function(errors) {
expect(errors).not.to.exist;
return foo.validate().then(function(errors) {
expect(errors).not.to.exist;
});
return expect(foo.validate()).not.to.be.rejected.then(function() {
return expect(foo.validate()).not.to.be.rejected;
});
});
......@@ -505,8 +488,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
var failingBar = Bar.build({ field: 'value3' });
return failingBar.validate().then(function(errors) {
expect(errors).not.to.be.null;
return expect(failingBar.validate()).to.be.rejected.then(function(errors) {
expect(errors.get('field')).to.have.length(1);
expect(errors.get('field')[0].message).to.equal('Validation isIn failed');
});
......@@ -527,9 +509,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
var failingBar = Bar.build({ field: 'value3' });
return failingBar.validate({ skip: ['field'] }).then(function(errors) {
expect(errors).not.to.exist;
});
return expect(failingBar.validate({ skip: ['field'] })).not.to.be.rejected;
});
it('raises an error if saving a different value into an immutable field', function() {
......@@ -546,10 +526,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
return User.create({ name: 'RedCat' }).then(function(user) {
expect(user.getDataValue('name')).to.equal('RedCat');
user.setDataValue('name', 'YellowCat');
return user.save()
.catch(function(errors) {
expect(errors).to.not.be.null;
expect(errors).to.be.an.instanceOf(Error);
return expect(user.save()).to.be.rejected.then(function(errors) {
expect(errors.get('name')[0].message).to.eql('Validation isImmutable failed');
});
});
......@@ -570,9 +547,7 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
expect(user.getDataValue('name')).to.equal('RedCat');
user.setDataValue('name', 'YellowCat');
return user.validate().then(function(errors) {
expect(errors).not.to.be.ok;
});
return expect(user.validate()).not.to.be.rejected;
});
it('raises an error for array on a STRING', function() {
......@@ -582,11 +557,9 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User.build({
return expect(User.build({
email: ['iama', 'dummy.com']
}).validate().then(function(errors) {
expect(errors).to.be.an.instanceof(Sequelize.ValidationError);
});
}).validate()).to.be.rejectedWith(Sequelize.ValidationError);
});
it('raises an error for array on a STRING(20)', function() {
......@@ -596,11 +569,9 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User.build({
return expect(User.build({
email: ['iama', 'dummy.com']
}).validate().then(function(errors) {
expect(errors).to.be.an.instanceof(Sequelize.ValidationError);
});
}).validate()).to.be.rejectedWith(Sequelize.ValidationError);
});
it('raises an error for array on a TEXT', function() {
......@@ -610,11 +581,9 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User.build({
return expect(User.build({
email: ['iama', 'dummy.com']
}).validate().then(function(errors) {
expect(errors).to.be.an.instanceof(Sequelize.ValidationError);
});
}).validate()).to.be.rejectedWith(Sequelize.ValidationError);
});
it('raises an error for {} on a STRING', function() {
......@@ -624,11 +593,9 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User.build({
return expect(User.build({
email: {lol: true}
}).validate().then(function(errors) {
expect(errors).to.be.an.instanceof(Sequelize.ValidationError);
});
}).validate()).to.be.rejectedWith(Sequelize.ValidationError);
});
it('raises an error for {} on a STRING(20)', function() {
......@@ -638,11 +605,9 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User.build({
return expect(User.build({
email: {lol: true}
}).validate().then(function(errors) {
expect(errors).to.be.an.instanceof(Sequelize.ValidationError);
});
}).validate()).to.be.rejectedWith(Sequelize.ValidationError);
});
it('raises an error for {} on a TEXT', function() {
......@@ -652,11 +617,9 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User.build({
return expect(User.build({
email: {lol: true}
}).validate().then(function(errors) {
expect(errors).to.be.an.instanceof(Sequelize.ValidationError);
});
}).validate()).to.be.rejectedWith(Sequelize.ValidationError);
});
it('does not raise an error for null on a STRING (where null is allowed)', function() {
......@@ -666,11 +629,9 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User.build({
return expect(User.build({
email: null
}).validate().then(function(errors) {
expect(errors).not.to.be.ok;
});
}).validate()).not.to.be.rejected;
});
it('validates VIRTUAL fields', function() {
......@@ -694,19 +655,16 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
});
return Sequelize.Promise.all([
User.build({
expect(User.build({
password: 'short',
salt: '42'
}).validate().then(function(errors) {
expect(errors).not.to.be.null;
}).validate()).to.be.rejected.then(function(errors) {
expect(errors.get('password')[0].message).to.equal('Please choose a longer password');
}),
User.build({
expect(User.build({
password: 'loooooooong',
salt: '42'
}).validate().then(function(errors) {
expect(errors).to.be.null;
})
}).validate()).not.to.be.rejected
]);
});
......@@ -724,14 +682,12 @@ describe(Support.getTestDialectTeaser('InstanceValidator'), function() {
}
});
return User.build({
return expect(User.build({
name: 'abcdefg'
}).validate().then(function(errors) {
expect(errors).to.be.null;
return User.build({
}).validate()).not.to.be.rejected.then(function() {
return expect(User.build({
name: 'a'
}).validate();
}).validate()).to.be.rejected;
}).then(function(errors) {
expect(errors.get('name')[0].message).to.equal('Validation isExactly7Characters failed');
});
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!