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

Commit 001b1567 by Sascha Depold

updated the validator to be async

2 parents 671718a5 41080488
...@@ -3,32 +3,52 @@ var Validator = require("validator") ...@@ -3,32 +3,52 @@ var Validator = require("validator")
var DaoValidator = module.exports = function(model) { var DaoValidator = module.exports = function(model) {
this.model = model this.model = model
this.chainer = new Utils.QueryChainer()
} }
DaoValidator.prototype.validate = function() { DaoValidator.prototype.validate = function() {
return new Utils.CustomEventEmitter(function(emitter) {
validateAttributes.call(this)
validateModel.call(this)
this
.chainer
.run()
.success(function () {
emitter.emit('success')
})
.error(function(err) {
var errors = {} var errors = {}
errors = Utils._.extend(errors, validateAttributes.call(this)) Utils._.each(err, function (value) {
errors = Utils._.extend(errors, validateModel.call(this)) Utils._.extend(errors, value)
})
return errors emitter.emit('success', errors)
})
}.bind(this)).run()
} }
// private // private
var validateModel = function() { var validateModel = function() {
var errors = {} Utils._.each(this.model.__options.validate, function(_validator, validatorType) {
var validator = prepareValidationOfAttribute.call(this, undefined, _validator, validatorType, { omitValue: true })
// for each model validator for this DAO
Utils._.each(this.model.__options.validate, function(validator, validatorType) { this.chainer.add(new Utils.CustomEventEmitter(function(emitter) {
try { var next = function(err) {
validator.apply(this.model) if (err) {
} catch (err) { var error = {}
errors[validatorType] = [err.message] // TODO: data structure needs to change for 2.0 error[validatorType] = [err]
emitter.emit('error', error)
} else {
emitter.emit('success')
}
} }
validator.args.unshift(next);
validator.fn.apply(null, validator.args)
}.bind(this)).run())
}.bind(this)) }.bind(this))
return errors
} }
var validateAttributes = function() { var validateAttributes = function() {
...@@ -48,32 +68,27 @@ var validateAttributes = function() { ...@@ -48,32 +68,27 @@ var validateAttributes = function() {
} }
var validateAttribute = function(value, field) { var validateAttribute = function(value, field) {
var errors = {}
// for each validator // for each validator
Utils._.each(this.model.validators[field], function(details, validatorType) { Utils._.each(this.model.validators[field], function(details, validatorType) {
var validator = prepareValidationOfAttribute.call(this, value, details, validatorType) var validator = prepareValidationOfAttribute.call(this, value, details, validatorType)
try { this.chainer.add(new Utils.CustomEventEmitter(function(emitter) {
validator.fn.apply(null, validator.args) var next = function(err) {
} catch (err) { if (err) {
var msg = err.message var error = {}
error[field] = [err]
// if we didn't provide a custom error message then augment the default one returned by the validator emitter.emit('error', error)
if (!validator.msg && !validator.isCustom) { } else {
msg += ": " + field emitter.emit('success')
} }
// each field can have multiple validation errors stored against it
errors[field] = errors[field] || []
errors[field].push(msg)
} }
validator.args.unshift(next);
validator.fn.apply(null, validator.args)
}.bind(this)).run())
}.bind(this)) // for each validator for this field }.bind(this)) // for each validator for this field
return errors
} }
var prepareValidationOfAttribute = function(value, details, validatorType) { var prepareValidationOfAttribute = function(value, details, validatorType, options) {
var isCustomValidator = false // if true then it's a custom validation method var isCustomValidator = false // if true then it's a custom validation method
, validatorFunction = null // the validation function to call , validatorFunction = null // the validation function to call
, validatorArgs = [] // extra arguments to pass to validation function , validatorArgs = [] // extra arguments to pass to validation function
...@@ -82,7 +97,9 @@ var prepareValidationOfAttribute = function(value, details, validatorType) { ...@@ -82,7 +97,9 @@ var prepareValidationOfAttribute = function(value, details, validatorType) {
if (typeof details === 'function') { if (typeof details === 'function') {
// it is a custom validator function? // it is a custom validator function?
isCustomValidator = true isCustomValidator = true
validatorFunction = Utils._.bind(details, this.model, value) validatorFunction = function(next) {
details.apply(this.model, ((options || {}).omitValue) ? [next] : [value, next])
}.bind(this)
} else { } else {
// it is a validator module function? // it is a validator module function?
...@@ -105,7 +122,16 @@ var prepareValidationOfAttribute = function(value, details, validatorType) { ...@@ -105,7 +122,16 @@ var prepareValidationOfAttribute = function(value, details, validatorType) {
} }
// bind to validator obj // bind to validator obj
validatorFunction = Utils._.bind(validator[validatorType], validator) validatorFunction = function(next) {
var args = Array.prototype.slice.call(arguments, 1)
try {
validator[validatorType].apply(validator, args)
next()
} catch (err) {
next(err.message)
}
}
} }
return { return {
......
...@@ -148,15 +148,15 @@ module.exports = (function() { ...@@ -148,15 +148,15 @@ module.exports = (function() {
this.dataValues[updatedAtAttr] = values[updatedAtAttr] = Utils.now() this.dataValues[updatedAtAttr] = values[updatedAtAttr] = Utils.now()
} }
var errors = this.validate()
if (!!errors) {
return new Utils.CustomEventEmitter(function(emitter) { return new Utils.CustomEventEmitter(function(emitter) {
this.validate().success(function(errors) {
if (!!errors) {
emitter.emit('error', errors) emitter.emit('error', errors)
}).run() } else if (this.isNewRecord) {
} this
else if (this.isNewRecord) { .QueryInterface
return this.QueryInterface.insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values) .insert(this, this.QueryInterface.QueryGenerator.addSchema(this.__factory), values)
.proxy(emitter)
} else { } else {
var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id; var identifier = this.__options.hasPrimaryKeys ? this.primaryKeyValues : this.id;
...@@ -167,8 +167,10 @@ module.exports = (function() { ...@@ -167,8 +167,10 @@ module.exports = (function() {
var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory) var tableName = this.QueryInterface.QueryGenerator.addSchema(this.__factory)
, query = this.QueryInterface.update(this, tableName, values, identifier) , query = this.QueryInterface.update(this, tableName, values, identifier)
return query query.proxy(emitter)
} }
}.bind(this))
}.bind(this)).run()
} }
/* /*
...@@ -209,13 +211,9 @@ module.exports = (function() { ...@@ -209,13 +211,9 @@ module.exports = (function() {
* @return null if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries. * @return null if and only if validation successful; otherwise an object containing { field name : [error msgs] } entries.
*/ */
DAO.prototype.validate = function() { DAO.prototype.validate = function() {
var validator = new DaoValidator(this) return new DaoValidator(this).validate()
, errors = validator.validate()
return (Utils._.isEmpty(errors) ? null : errors)
} }
DAO.prototype.updateAttributes = function(updates, fields) { DAO.prototype.updateAttributes = function(updates, fields) {
this.setAttributes(updates) this.setAttributes(updates)
return this.save(fields) return this.save(fields)
......
...@@ -6,13 +6,13 @@ var util = require("util") ...@@ -6,13 +6,13 @@ var util = require("util")
var bindToProcess = function(fct) { var bindToProcess = function(fct) {
if (fct) { if (fct) {
if(process.domain) { if (process.domain) {
return process.domain.bind(fct); return process.domain.bind(fct)
} }
} }
return fct; return fct
}; }
module.exports = (function() { module.exports = (function() {
var CustomEventEmitter = function(fct) { var CustomEventEmitter = function(fct) {
...@@ -54,6 +54,7 @@ module.exports = (function() { ...@@ -54,6 +54,7 @@ module.exports = (function() {
return this return this
} }
// emit the events on the foreign emitter once events got triggered for me
CustomEventEmitter.prototype.proxy = function(emitter) { CustomEventEmitter.prototype.proxy = function(emitter) {
proxyEventKeys.forEach(function (eventKey) { proxyEventKeys.forEach(function (eventKey) {
this.on(eventKey, function (result) { this.on(eventKey, function (result) {
...@@ -73,7 +74,5 @@ module.exports = (function() { ...@@ -73,7 +74,5 @@ module.exports = (function() {
}).then(onFulfilled, onRejected) }).then(onFulfilled, onRejected)
} }
return CustomEventEmitter
return CustomEventEmitter;
})() })()
...@@ -201,7 +201,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -201,7 +201,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
for (var i = 0; i < validatorDetails.fail.length; i++) { for (var i = 0; i < validatorDetails.fail.length; i++) {
var failingValue = validatorDetails.fail[i] var failingValue = validatorDetails.fail[i]
it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function() { it('correctly specifies an instance as invalid using a value of "' + failingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {} var validations = {}
, message = validator + "(" + failingValue + ")" , message = validator + "(" + failingValue + ")"
...@@ -221,11 +221,15 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -221,11 +221,15 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
}) })
var failingUser = UserFail.build({ name : failingValue }) var failingUser = UserFail.build({ name : failingValue })
, errors = failingUser.validate() , errors = undefined;
expect(errors).not.toBeNull()
expect(errors).toEqual({ name : [message] }) failingUser.validate().done( function(err,_errors) {
}) expect(_errors).not.toBeNull();
expect(_errors).toEqual({ name : [message] });
done();
});
});
} }
//////////////////////////// ////////////////////////////
...@@ -234,7 +238,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -234,7 +238,7 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
for (var j = 0; j < validatorDetails.pass.length; j++) { for (var j = 0; j < validatorDetails.pass.length; j++) {
var succeedingValue = validatorDetails.pass[j] var succeedingValue = validatorDetails.pass[j]
it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function() { it('correctly specifies an instance as valid using a value of "' + succeedingValue + '" for the validation "' + validator + '"', function(done) {
var validations = {} var validations = {}
if (validatorDetails.hasOwnProperty('spec')) { if (validatorDetails.hasOwnProperty('spec')) {
...@@ -252,21 +256,30 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -252,21 +256,30 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
} }
}) })
var successfulUser = UserSuccess.build({ name: succeedingValue }) var successfulUser = UserSuccess.build({ name: succeedingValue });
expect(successfulUser.validate()).toBeNull()
successfulUser.validate().success( function() {
expect(arguments.length).toBe(0);
done();
}).error(function(err) {
expect(err).toBe({})
done();
});
}) })
} }
} }
} }
it('correctly validates using custom validation methods', function() { it('correctly validates using custom validation methods', function(done) {
var User = this.sequelize.define('User' + Math.random(), { var User = this.sequelize.define('User' + Math.random(), {
name: { name: {
type: Sequelize.STRING, type: Sequelize.STRING,
validate: { validate: {
customFn: function(val) { customFn: function(val,next) {
if (val !== "2") { if (val !== "2") {
throw new Error("name should equal '2'") next("name should equal '2'")
} else {
next()
} }
} }
} }
...@@ -274,16 +287,23 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -274,16 +287,23 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
}) })
var failingUser = User.build({ name : "3" }) var failingUser = User.build({ name : "3" })
, errors = failingUser.validate()
expect(errors).not.toBeNull(null) failingUser.validate().error(function(errors) {
expect(errors).toEqual({ name: ["name should equal '2'"] }) expect(errors).toEqual({ name: ["name should equal '2'"] })
var successfulUser = User.build({ name : "2" }) var successfulUser = User.build({ name : "2" })
expect(successfulUser.validate()).toBeNull() successfulUser.validate().success(function() {
expect(arguments.length).toBe(0);
done();
}).error(function(err) {
expect(err).toBe({});
done();
})
});
}) })
it('skips other validations if allowNull is true and the value is null', function() { it('skips other validations if allowNull is true and the value is null', function(done) {
var User = this.sequelize.define('User' + Math.random(), { var User = this.sequelize.define('User' + Math.random(), {
age: { age: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -294,20 +314,22 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -294,20 +314,22 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
} }
}) })
var failingUser = User.build({ age: -1 }) User
, errors = failingUser.validate() .build({ age: -1 })
.validate()
.success(function(errors) {
expect(errors).not.toBeNull(null) expect(errors).not.toBeNull(null)
expect(errors).toEqual({ age: ['must be positive'] }) expect(errors).toEqual({ age: ['must be positive'] })
var successfulUser1 = User.build({ age: null }) User.build({ age: null }).validate().success(function() {
expect(successfulUser1.validate()).toBeNull() User.build({ age: 1 }).validate().success(function() {
done()
var successfulUser2 = User.build({ age: 1 }) })
expect(successfulUser2.validate()).toBeNull() })
})
}) })
it('validates a model with custom model-wide validation methods', function() { it('validates a model with custom model-wide validation methods', function(done) {
var Foo = this.sequelize.define('Foo' + Math.random(), { var Foo = this.sequelize.define('Foo' + Math.random(), {
field1: { field1: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
...@@ -319,22 +341,32 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() { ...@@ -319,22 +341,32 @@ describe(Helpers.getTestDialectTeaser("DaoValidator"), function() {
} }
}, { }, {
validate: { validate: {
xnor: function() { xnor: function(done) {
if ((this.field1 === null) === (this.field2 === null)) { if ((this.field1 === null) === (this.field2 === null)) {
throw new Error('xnor failed'); done('xnor failed')
} else {
done()
} }
} }
} }
}) })
var failingFoo = Foo.build({ field1: null, field2: null }) Foo
, errors = failingFoo.validate() .build({ field1: null, field2: null })
.validate()
.success(function(errors) {
expect(errors).not.toBeNull() expect(errors).not.toBeNull()
expect(errors).toEqual({ 'xnor': ['xnor failed'] }) expect(errors).toEqual({ 'xnor': ['xnor failed'] })
var successfulFoo = Foo.build({ field1: 33, field2: null }) Foo
expect(successfulFoo.validate()).toBeNull() .build({ field1: 33, field2: null })
.validate()
.success(function(errors) {
console.log(errors)
expect(errors).not.toBeDefined()
done()
})
})
}) })
}) })
}) })
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!