dao-validator.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. var Validator = require("validator")
  2. , Utils = require("./utils")
  3. // Backwards compat for people using old validation function
  4. // We cannot use .extend, since it coerces the first arg to string
  5. Validator.notNull = function (val) {
  6. return [null, undefined].indexOf(val) === -1
  7. }
  8. // https://github.com/chriso/validator.js/blob/1.5.0/lib/validators.js
  9. Validator.extend('notEmpty', function(str) {
  10. return !str.match(/^[\s\t\r\n]*$/);
  11. })
  12. Validator.extend('len', function(str, min, max) {
  13. return this.isLength(str, min, max)
  14. })
  15. Validator.extend('isUrl', function(str) {
  16. return this.isURL(str)
  17. })
  18. Validator.extend('isIPv6', function(str) {
  19. return this.isIP(str, 6)
  20. })
  21. Validator.extend('isIPv4', function(str) {
  22. return this.isIP(str, 4)
  23. })
  24. Validator.extend('notIn', function(str, values) {
  25. return !this.isIn(str, values)
  26. })
  27. Validator.extend('regex', function(str, pattern, modifiers) {
  28. str += '';
  29. if (Object.prototype.toString.call(pattern).slice(8, -1) !== 'RegExp') {
  30. pattern = new RegExp(pattern, modifiers);
  31. }
  32. return str.match(pattern);
  33. })
  34. Validator.extend('notRegex', function(str, pattern, modifiers) {
  35. return !this.regex(str, pattern, modifiers);
  36. })
  37. Validator.extend('isDecimal', function(str) {
  38. return str !== '' && str.match(/^(?:-?(?:[0-9]+))?(?:\.[0-9]*)?(?:[eE][\+\-]?(?:[0-9]+))?$/);
  39. })
  40. Validator.extend('min', function(str, val) {
  41. var number = parseFloat(str);
  42. return isNaN(number) || number >= val;
  43. })
  44. Validator.extend('max', function(str, val) {
  45. var number = parseFloat(str);
  46. return isNaN(number) || number <= val;
  47. })
  48. Validator.extend('not', function(str, pattern, modifiers) {
  49. return this.notRegex(str, pattern, modifiers);
  50. })
  51. Validator.extend('contains', function(str, elem) {
  52. return str.indexOf(elem) >= 0 && !!elem;
  53. })
  54. Validator.extend('notContains', function(str, elem) {
  55. return !this.contains(str, elem);
  56. })
  57. Validator.extend('is', function(str, pattern, modifiers) {
  58. return this.regex(str, pattern, modifiers);
  59. })
  60. var DaoValidator = module.exports = function(model, options) {
  61. options = options || {}
  62. options.skip = options.skip || []
  63. this.model = model
  64. this.options = options
  65. /**
  66. * Expose validator.js to allow users to extend
  67. * @name Validator
  68. */
  69. this.Validator = Validator
  70. }
  71. DaoValidator.prototype.validate = function() {
  72. var errors = {}
  73. errors = Utils._.extend(errors, validateAttributes.call(this))
  74. errors = Utils._.extend(errors, validateModel.call(this))
  75. return errors
  76. }
  77. DaoValidator.prototype.hookValidate = function() {
  78. var self = this
  79. , errors = {}
  80. return new Utils.CustomEventEmitter(function(emitter) {
  81. self.model.daoFactory.runHooks('beforeValidate', self.model, function(err) {
  82. if (!!err) {
  83. return emitter.emit('error', err)
  84. }
  85. errors = Utils._.extend(errors, validateAttributes.call(self))
  86. errors = Utils._.extend(errors, validateModel.call(self))
  87. if (Object.keys(errors).length > 0) {
  88. return emitter.emit('error', errors)
  89. }
  90. self.model.daoFactory.runHooks('afterValidate', self.model, function(err) {
  91. if (!!err) {
  92. return emitter.emit('error', err)
  93. }
  94. emitter.emit('success', self.model)
  95. })
  96. })
  97. }).run()
  98. }
  99. // private
  100. var validateModel = function() {
  101. var self = this
  102. , errors = {}
  103. // for each model validator for this DAO
  104. Utils._.each(this.model.__options.validate, function(validator, validatorType) {
  105. try {
  106. validator.apply(self.model)
  107. } catch (err) {
  108. errors[validatorType] = [err.message] // TODO: data structure needs to change for 2.0
  109. }
  110. })
  111. return errors
  112. }
  113. var validateAttributes = function() {
  114. var self = this
  115. , errors = {}
  116. Utils._.each(this.model.rawAttributes, function(rawAttribute, field) {
  117. var value = self.model.dataValues[field]
  118. , hasAllowedNull = ((rawAttribute === undefined || rawAttribute.allowNull === true) && ((value === null) || (value === undefined)))
  119. , isSkipped = self.options.skip.length > 0 && self.options.skip.indexOf(field) !== -1
  120. if (self.model.validators.hasOwnProperty(field) && !hasAllowedNull && !isSkipped) {
  121. errors = Utils._.merge(errors, validateAttribute.call(self, value, field))
  122. }
  123. })
  124. return errors
  125. }
  126. var validateAttribute = function(value, field) {
  127. var self = this
  128. , errors = {}
  129. // for each validator
  130. Utils._.each(this.model.validators[field], function(details, validatorType) {
  131. var validator = prepareValidationOfAttribute.call(self, value, details, validatorType)
  132. try {
  133. validator.fn.apply(null, validator.args)
  134. } catch (err) {
  135. var msg = err.message
  136. // if we didn't provide a custom error message then augment the default one returned by the validator
  137. if (!validator.msg && !validator.isCustom) {
  138. msg += ": " + field
  139. }
  140. // each field can have multiple validation errors stored against it
  141. errors[field] = errors[field] || []
  142. errors[field].push(msg)
  143. }
  144. })
  145. return errors
  146. }
  147. var prepareValidationOfAttribute = function(value, details, validatorType) {
  148. var isCustomValidator = false // if true then it's a custom validation method
  149. , validatorFunction = null // the validation function to call
  150. , validatorArgs = [] // extra arguments to pass to validation function
  151. , errorMessage = "" // the error message to return if validation fails
  152. if (typeof details === 'function') {
  153. // it is a custom validator function?
  154. isCustomValidator = true
  155. validatorFunction = Utils._.bind(details, this.model, value)
  156. } else {
  157. // it is a validator module function?
  158. // extract extra arguments for the validator
  159. validatorArgs = details.hasOwnProperty("args") ? details.args : details
  160. if (!Array.isArray(validatorArgs)) {
  161. validatorArgs = [validatorArgs]
  162. }
  163. // extract the error msg
  164. errorMessage = details.hasOwnProperty("msg") ? details.msg : undefined
  165. // check if Validator knows that kind of validation test
  166. if (!Utils._.isFunction(Validator[validatorType])) {
  167. throw new Error("Invalid validator function: " + validatorType)
  168. }
  169. validatorFunction = function () {
  170. if (!Validator[validatorType].apply(null, [value].concat(validatorArgs))) {
  171. throw new Error(errorMessage || "Validation "+validatorType+" failed")
  172. }
  173. }
  174. }
  175. return {
  176. fn: validatorFunction,
  177. msg: errorMessage,
  178. args: validatorArgs,
  179. isCustom: isCustomValidator
  180. }
  181. }