-
Notifications
You must be signed in to change notification settings - Fork 44
/
mongoose-validator.js
168 lines (147 loc) · 5.09 KB
/
mongoose-validator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**
* Validators for Mongoose.js utilising validator.js
* @module mongoose-validator
* @author Lee Powell lee@leepowell.co.uk
* @copyright MIT
*/
const validatorjs = require('validator')
const is = require('is')
const defaultErrorMessages = require('./default-error-messages.json')
const customValidators = {}
const customErrorMessages = {}
const omit = function omit(obj, ...keys) {
return Object.keys(obj)
.filter(key => !keys.includes(key))
.reduce((acc, val) => {
acc[val] = obj[val]
return acc
}, {})
}
const getValidatorFn = function getValidatorFn(validator) {
// Validator has been passed as a function so just return it
if (is.function(validator)) {
return validator
}
// Validator has been passed as a string (i.e. 'isLength'), try to find the validator is validator.js or custom validators
if (is.string(validator) && !is.empty(validator)) {
return validatorjs[validator] || customValidators[validator] || undefined
}
}
const toArray = (val = []) => (is.array(val) ? val : Array.of(val))
const findFirstString = (...vals) => vals.filter(is.string).shift()
const interpolateMessageWithArgs = (message = '', args = []) =>
message.replace(/{ARGS\[(\d+)\]}/g, (match, submatch) => args[submatch] || '')
const createValidator = function createValidator(fn, args, passIfEmpty) {
return function validator(val) {
const validatorArgs = [val].concat(args)
if ((passIfEmpty && (is.empty(val) || is.nil(val))) || is.undef(val)) {
return true
}
return fn.apply(this, validatorArgs)
}
}
/**
* Create a validator object
*
* @alias module:mongoose-validator
*
* @param {object} options Options object
* @param {string} options.validator Validator name to use
* @param {*} [options.arguments=[]] Arguments to pass to validator. If more than one argument is required an array must be used. Single arguments will internally be coerced into an array
* @param {boolean} [options.passIfEmpty=false] Weather the validator should pass if the value being validated is empty
* @param {string} [options.message=Error] Validator error message
*
* @return {object} Returns validator compatible with mongoosejs
*
* @throws If validator option property is not defined
* @throws If validator option is not a function or string
* @throws If validator option is a validator method (string) and method does not exist in validate.js or as a custom validator
*
* @example
* require('mongoose-validator').validate({ validator: 'isLength', arguments: [4, 40], passIfEmpty: true, message: 'Value should be between 4 and 40 characters' )
*/
const validate = function validate(options) {
if (is.undef(options.validator)) {
throw new Error('validator option undefined')
}
if (!is.function(options.validator) && !is.string(options.validator)) {
throw new Error(
`validator must be of type function or string, received ${typeof options.validator}`
)
}
const validatorName = is.string(options.validator) ? options.validator : ''
const validatorFn = getValidatorFn(options.validator)
if (is.undef(validatorFn)) {
throw new Error(
`validator \`${validatorName}\` does not exist in validator.js or as a custom validator`
)
}
const passIfEmpty = !!options.passIfEmpty
const mongooseOpts = omit(
options,
'passIfEmpty',
'message',
'validator',
'arguments'
)
const args = toArray(options.arguments)
const messageStr = findFirstString(
options.message,
customErrorMessages[validatorName],
defaultErrorMessages[validatorName],
'Error'
)
const message = interpolateMessageWithArgs(messageStr, args)
const validator = createValidator(validatorFn, args, passIfEmpty)
return Object.assign(
{
validator,
message,
},
mongooseOpts
)
}
/**
* Extend the mongoose-validator with a custom validator
*
* @param {string} name Validator method name
* @param {function} fn Validator method function
* @param {string} [msg=Error] Validator error message
*
* @return {undefined}
*
* @throws If name is not a string
* @throws If validator is not a function
* @throws If message is not a string
* @throws If name is empty i.e ''
* @throws If a validator of the same method name already exists
*
* @example
* require('mongoose-validator').extend('isString', function (str) { return typeof str === 'string' }, 'Not a string')
*/
validate.extend = function extend(name, fn, msg = 'Error') {
if (typeof name !== 'string') {
throw new Error(`name must be a string, received ${typeof name}`)
}
if (typeof fn !== 'function') {
throw new Error(`validator must be a function, received ${typeof fn}`)
}
if (typeof msg !== 'string') {
throw new Error(`message must be a string, received ${typeof msg}`)
}
if (name === '') {
throw new Error('name is required')
}
if (customValidators[name]) {
throw new Error(`validator \`${name}\` already exists`)
}
customValidators[name] = function(...args) {
return fn.apply(this, args)
}
customErrorMessages[name] = msg
}
/**
* Default error messages
*/
validate.defaultErrorMessages = defaultErrorMessages
module.exports = validate