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

Commit a49a7c1d by Jan Aagaard Meier

Merge pull request #5196 from skleeschulte/master

Added validationFailed hook
2 parents efc432b9 bc30943c
# Future # Future
- [ADDED] `beforeCount` hook [#5209](https://github.com/sequelize/sequelize/pull/5209) - [ADDED] `beforeCount` hook [#5209](https://github.com/sequelize/sequelize/pull/5209)
- [ADDED] `validationFailed` hook [#1626](https://github.com/sequelize/sequelize/issues/1626)
# 3.19.3 # 3.19.3
- [FIXED] `updatedAt` and `createdAt` values are now set before validation [#5367](https://github.com/sequelize/sequelize/pull/5367) - [FIXED] `updatedAt` and `createdAt` values are now set before validation [#5367](https://github.com/sequelize/sequelize/pull/5367)
......
...@@ -15,6 +15,8 @@ For a full list of hooks, see [Hooks API](/api/hooks). ...@@ -15,6 +15,8 @@ For a full list of hooks, see [Hooks API](/api/hooks).
validate validate
(3) (3)
afterValidate(instance, options, fn) afterValidate(instance, options, fn)
- or -
validationFailed(instance, options, error, fn)
(4) (4)
beforeCreate(instance, options, fn) beforeCreate(instance, options, fn)
beforeDestroy(instance, options, fn) beforeDestroy(instance, options, fn)
...@@ -159,7 +161,7 @@ The following hooks will emit whenever you're editing a single object ...@@ -159,7 +161,7 @@ The following hooks will emit whenever you're editing a single object
``` ```
beforeValidate beforeValidate
afterValidate afterValidate or validationFailed
beforeCreate / beforeUpdate / beforeDestroy beforeCreate / beforeUpdate / beforeDestroy
afterCreate / afterUpdate / afterDestroy afterCreate / afterUpdate / afterDestroy
``` ```
......
...@@ -39,6 +39,7 @@ var Utils = require('./utils') ...@@ -39,6 +39,7 @@ var Utils = require('./utils')
var hookTypes = { var hookTypes = {
beforeValidate: {params: 2}, beforeValidate: {params: 2},
afterValidate: {params: 2}, afterValidate: {params: 2},
validationFailed: {params: 3},
beforeCreate: {params: 2}, beforeCreate: {params: 2},
afterCreate: {params: 2}, afterCreate: {params: 2},
beforeDestroy: {params: 2}, beforeDestroy: {params: 2},
...@@ -225,6 +226,14 @@ Hooks.hasHooks = Hooks.hasHook; ...@@ -225,6 +226,14 @@ Hooks.hasHooks = Hooks.hasHook;
*/ */
/** /**
* A hook that is run when validation fails
* @param {String} name
* @param {Function} fn A callback function that is called with instance, options, error. Error is the
* SequelizeValidationError. If the callback throws an error, it will replace the original validation error.
* @name validationFailed
*/
/**
* A hook that is run before creating a single instance * A hook that is run before creating a single instance
* @param {String} name * @param {String} name
* @param {Function} fn A callback function that is called with attributes, options * @param {Function} fn A callback function that is called with attributes, options
......
...@@ -80,7 +80,8 @@ InstanceValidator.prototype.validate = function() { ...@@ -80,7 +80,8 @@ InstanceValidator.prototype.validate = function() {
* Invoke the Validation sequence: * Invoke the Validation sequence:
* - Before Validation Model Hooks * - Before Validation Model Hooks
* - Validation * - Validation
* - After Validation Model Hooks * - On validation success: After Validation Model Hooks
* - On validation failure: Validation Failed Model Hooks
* *
* @return {Promise} * @return {Promise}
*/ */
...@@ -89,7 +90,9 @@ InstanceValidator.prototype.hookValidate = function() { ...@@ -89,7 +90,9 @@ InstanceValidator.prototype.hookValidate = function() {
return self.modelInstance.Model.runHooks('beforeValidate', self.modelInstance, self.options).then(function() { return self.modelInstance.Model.runHooks('beforeValidate', self.modelInstance, self.options).then(function() {
return self.validate().then(function(error) { return self.validate().then(function(error) {
if (error) { if (error) {
throw error; return self.modelInstance.Model.runHooks('validationFailed', self.modelInstance, self.options, error).then(function(newError) {
throw newError || error;
});
} }
}); });
}).then(function() { }).then(function() {
......
...@@ -561,7 +561,7 @@ Sequelize.prototype.getQueryInterface = function() { ...@@ -561,7 +561,7 @@ Sequelize.prototype.getQueryInterface = function() {
* @param {String} [options.comment] * @param {String} [options.comment]
* @param {String} [options.collate] * @param {String} [options.collate]
* @param {String} [options.initialAutoIncrement] Set the initial AUTO_INCREMENT value for the table in MySQL. * @param {String} [options.initialAutoIncrement] Set the initial AUTO_INCREMENT value for the table in MySQL.
* @param {Object} [options.hooks] An object of hook function that are called before and after certain lifecycle events. The possible hooks are: beforeValidate, afterValidate, beforeBulkCreate, beforeBulkDestroy, beforeBulkUpdate, beforeCreate, beforeDestroy, beforeUpdate, afterCreate, afterDestroy, afterUpdate, afterBulkCreate, afterBulkDestory and afterBulkUpdate. See Hooks for more information about hook functions and their signatures. Each property can either be a function, or an array of functions. * @param {Object} [options.hooks] An object of hook function that are called before and after certain lifecycle events. The possible hooks are: beforeValidate, afterValidate, validationFailed, beforeBulkCreate, beforeBulkDestroy, beforeBulkUpdate, beforeCreate, beforeDestroy, beforeUpdate, afterCreate, afterDestroy, afterUpdate, afterBulkCreate, afterBulkDestory and afterBulkUpdate. See Hooks for more information about hook functions and their signatures. Each property can either be a function, or an array of functions.
* @param {Object} [options.validate] An object of model wide validations. Validations have access to all model values via `this`. If the validator function takes an argument, it is assumed to be async, and is called with a callback that accepts an optional error. * @param {Object} [options.validate] An object of model wide validations. Validations have access to all model values via `this`. If the validator function takes an argument, it is assumed to be async, and is called with a callback that accepts an optional error.
* *
* @return {Model} * @return {Model}
......
...@@ -12,7 +12,10 @@ var chai = require('chai') ...@@ -12,7 +12,10 @@ var chai = require('chai')
describe(Support.getTestDialectTeaser('Hooks'), function() { describe(Support.getTestDialectTeaser('Hooks'), function() {
beforeEach(function() { beforeEach(function() {
this.User = this.sequelize.define('User', { this.User = this.sequelize.define('User', {
username: DataTypes.STRING, username: {
type: DataTypes.STRING,
allowNull: false
},
mood: { mood: {
type: DataTypes.ENUM, type: DataTypes.ENUM,
values: ['happy', 'sad', 'neutral'] values: ['happy', 'sad', 'neutral']
...@@ -36,6 +39,7 @@ describe(Support.getTestDialectTeaser('Hooks'), function() { ...@@ -36,6 +39,7 @@ describe(Support.getTestDialectTeaser('Hooks'), function() {
describe('#create', function() { describe('#create', function() {
it('should return the user', function() { it('should return the user', function() {
this.User.beforeValidate(function(user, options) { this.User.beforeValidate(function(user, options) {
user.username = 'Bob';
user.mood = 'happy'; user.mood = 'happy';
}); });
...@@ -57,7 +61,37 @@ describe(Support.getTestDialectTeaser('Hooks'), function() { ...@@ -57,7 +61,37 @@ describe(Support.getTestDialectTeaser('Hooks'), function() {
throw new Error('Whoops! Changed user.mood!'); throw new Error('Whoops! Changed user.mood!');
}); });
return expect(this.User.create({mood: 'happy'})).to.be.rejectedWith('Whoops! Changed user.mood!'); return expect(this.User.create({username: 'Toni', mood: 'happy'})).to.be.rejectedWith('Whoops! Changed user.mood!');
});
it('should call validationFailed hook', function() {
var validationFailedHook = sinon.spy();
this.User.validationFailed(validationFailedHook);
return expect(this.User.create({mood: 'happy'})).to.be.rejected.then(function(err) {
expect(validationFailedHook).to.have.been.calledOnce;
});
});
it('should not replace the validation error in validationFailed hook by default', function() {
var validationFailedHook = sinon.stub();
this.User.validationFailed(validationFailedHook);
return expect(this.User.create({mood: 'happy'})).to.be.rejected.then(function(err) {
expect(err.name).to.equal('SequelizeValidationError');
});
});
it('should replace the validation error if validationFailed hook creates a new error', function() {
var validationFailedHook = sinon.stub().throws(new Error('Whoops!'));
this.User.validationFailed(validationFailedHook);
return expect(this.User.create({mood: 'happy'})).to.be.rejected.then(function(err) {
expect(err.message).to.equal('Whoops!');
});
}); });
}); });
}); });
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!