validation-error.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. 'use strict';
  2. const BaseError = require('./base-error');
  3. /**
  4. * Validation Error. Thrown when the sequelize validation has failed. The error contains an `errors` property,
  5. * which is an array with 1 or more ValidationErrorItems, one for each validation that failed.
  6. *
  7. * @param {string} message Error message
  8. * @param {Array} [errors] Array of ValidationErrorItem objects describing the validation errors
  9. *
  10. * @property errors {ValidationErrorItems[]}
  11. */
  12. class ValidationError extends BaseError {
  13. constructor(message, errors) {
  14. super(message);
  15. this.name = 'SequelizeValidationError';
  16. this.message = 'Validation Error';
  17. /**
  18. *
  19. * @type {ValidationErrorItem[]}
  20. */
  21. this.errors = errors || [];
  22. // Use provided error message if available...
  23. if (message) {
  24. this.message = message;
  25. // ... otherwise create a concatenated message out of existing errors.
  26. } else if (this.errors.length > 0 && this.errors[0].message) {
  27. this.message = this.errors.map(err => `${err.type || err.origin}: ${err.message}`).join(',\n');
  28. }
  29. Error.captureStackTrace(this, this.constructor);
  30. }
  31. /**
  32. * Gets all validation error items for the path / field specified.
  33. *
  34. * @param {string} path The path to be checked for error items
  35. *
  36. * @returns {Array<ValidationErrorItem>} Validation error items for the specified path
  37. */
  38. get(path) {
  39. return this.errors.reduce((reduced, error) => {
  40. if (error.path === path) {
  41. reduced.push(error);
  42. }
  43. return reduced;
  44. }, []);
  45. }
  46. }
  47. /**
  48. * Validation Error Item
  49. * Instances of this class are included in the `ValidationError.errors` property.
  50. */
  51. class ValidationErrorItem {
  52. /**
  53. * Creates new validation error item
  54. *
  55. * @param {string} message An error message
  56. * @param {string} type The type/origin of the validation error
  57. * @param {string} path The field that triggered the validation error
  58. * @param {string} value The value that generated the error
  59. * @param {Object} [inst] the DAO instance that caused the validation error
  60. * @param {Object} [validatorKey] a validation "key", used for identification
  61. * @param {string} [fnName] property name of the BUILT-IN validator function that caused the validation error (e.g. "in" or "len"), if applicable
  62. * @param {string} [fnArgs] parameters used with the BUILT-IN validator function, if applicable
  63. */
  64. constructor(message, type, path, value, inst, validatorKey, fnName, fnArgs) {
  65. /**
  66. * An error message
  67. *
  68. * @type {string} message
  69. */
  70. this.message = message || '';
  71. /**
  72. * The type/origin of the validation error
  73. *
  74. * @type {string}
  75. */
  76. this.type = null;
  77. /**
  78. * The field that triggered the validation error
  79. *
  80. * @type {string}
  81. */
  82. this.path = path || null;
  83. /**
  84. * The value that generated the error
  85. *
  86. * @type {string}
  87. */
  88. this.value = value !== undefined ? value : null;
  89. this.origin = null;
  90. /**
  91. * The DAO instance that caused the validation error
  92. *
  93. * @type {Model}
  94. */
  95. this.instance = inst || null;
  96. /**
  97. * A validation "key", used for identification
  98. *
  99. * @type {string}
  100. */
  101. this.validatorKey = validatorKey || null;
  102. /**
  103. * Property name of the BUILT-IN validator function that caused the validation error (e.g. "in" or "len"), if applicable
  104. *
  105. * @type {string}
  106. */
  107. this.validatorName = fnName || null;
  108. /**
  109. * Parameters used with the BUILT-IN validator function, if applicable
  110. *
  111. * @type {string}
  112. */
  113. this.validatorArgs = fnArgs || [];
  114. if (type) {
  115. if (ValidationErrorItem.Origins[ type ]) {
  116. this.origin = type;
  117. } else {
  118. const lowercaseType = `${type}`.toLowerCase().trim();
  119. const realType = ValidationErrorItem.TypeStringMap[ lowercaseType ];
  120. if (realType && ValidationErrorItem.Origins[ realType ]) {
  121. this.origin = realType;
  122. this.type = type;
  123. }
  124. }
  125. }
  126. // This doesn't need captureStackTrace because it's not a subclass of Error
  127. }
  128. /**
  129. * return a lowercase, trimmed string "key" that identifies the validator.
  130. *
  131. * Note: the string will be empty if the instance has neither a valid `validatorKey` property nor a valid `validatorName` property
  132. *
  133. * @param {boolean} [useTypeAsNS=true] controls whether the returned value is "namespace",
  134. * this parameter is ignored if the validator's `type` is not one of ValidationErrorItem.Origins
  135. * @param {string} [NSSeparator='.'] a separator string for concatenating the namespace, must be not be empty,
  136. * defaults to "." (fullstop). only used and validated if useTypeAsNS is TRUE.
  137. * @throws {Error} thrown if NSSeparator is found to be invalid.
  138. * @returns {string}
  139. *
  140. * @private
  141. */
  142. getValidatorKey(useTypeAsNS, NSSeparator) {
  143. const useTANS = useTypeAsNS === undefined || !!useTypeAsNS;
  144. const NSSep = NSSeparator === undefined ? '.' : NSSeparator;
  145. const type = this.origin;
  146. const key = this.validatorKey || this.validatorName;
  147. const useNS = useTANS && type && ValidationErrorItem.Origins[ type ];
  148. if (useNS && (typeof NSSep !== 'string' || !NSSep.length)) {
  149. throw new Error('Invalid namespace separator given, must be a non-empty string');
  150. }
  151. if (!(typeof key === 'string' && key.length)) {
  152. return '';
  153. }
  154. return (useNS ? [type, key].join(NSSep) : key).toLowerCase().trim();
  155. }
  156. }
  157. /**
  158. * An enum that defines valid ValidationErrorItem `origin` values
  159. *
  160. * @type {Object}
  161. * @property CORE {string} specifies errors that originate from the sequelize "core"
  162. * @property DB {string} specifies validation errors that originate from the storage engine
  163. * @property FUNCTION {string} specifies validation errors that originate from validator functions (both built-in and custom) defined for a given attribute
  164. */
  165. ValidationErrorItem.Origins = {
  166. CORE: 'CORE',
  167. DB: 'DB',
  168. FUNCTION: 'FUNCTION'
  169. };
  170. /**
  171. * An object that is used internally by the `ValidationErrorItem` class
  172. * that maps current `type` strings (as given to ValidationErrorItem.constructor()) to
  173. * our new `origin` values.
  174. *
  175. * @type {Object}
  176. */
  177. ValidationErrorItem.TypeStringMap = {
  178. 'notnull violation': 'CORE',
  179. 'string violation': 'CORE',
  180. 'unique violation': 'DB',
  181. 'validation error': 'FUNCTION'
  182. };
  183. module.exports = ValidationError;
  184. module.exports.ValidationErrorItem = ValidationErrorItem;