monastery 2.2.3 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +10 -1
- package/changelog.md +3 -1
- package/docs/_config.yml +2 -2
- package/docs/assets/imgs/monastery.jpg +0 -0
- package/docs/definition/index.md +1 -2
- package/docs/manager/index.md +19 -11
- package/docs/manager/model.md +1 -1
- package/docs/manager/models.md +2 -3
- package/docs/model/count.md +25 -0
- package/docs/model/find.md +5 -9
- package/docs/model/findOne.md +1 -1
- package/docs/model/findOneAndUpdate.md +1 -1
- package/docs/model/index.md +5 -30
- package/docs/model/insert.md +4 -6
- package/docs/model/rawMethods.md +290 -0
- package/docs/model/remove.md +4 -6
- package/docs/model/update.md +4 -6
- package/docs/readme.md +69 -48
- package/lib/collection.js +324 -0
- package/lib/index.js +207 -67
- package/lib/model-crud.js +605 -619
- package/lib/model-validate.js +227 -245
- package/lib/model.js +70 -91
- package/lib/rules.js +36 -35
- package/lib/util.js +69 -15
- package/package.json +12 -11
- package/plugins/images/index.js +11 -11
- package/test/blacklisting.js +506 -537
- package/test/collection.js +445 -0
- package/test/crud.js +810 -730
- package/test/index.test.js +26 -0
- package/test/manager.js +77 -0
- package/test/mock/blacklisting.js +23 -23
- package/test/model.js +611 -572
- package/test/plugin-images.js +880 -965
- package/test/populate.js +249 -262
- package/test/util.js +126 -45
- package/test/validate.js +1074 -1121
- package/test/virtuals.js +222 -227
- package/lib/monk-monkey-patches.js +0 -90
- package/test/monk.js +0 -53
- package/test/test.js +0 -38
package/lib/model-validate.js
CHANGED
|
@@ -1,263 +1,245 @@
|
|
|
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
|
-
let validated = this._validateFields(item, this.fields, item, opts, '')
|
|
48
|
-
if (validated[0].length) throw validated[0]
|
|
49
|
-
else return validated[1]
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
// Single document?
|
|
53
|
-
response = util.isArray(data)? response : response[0]
|
|
54
|
-
|
|
55
|
-
// Success/error
|
|
56
|
-
if (cb) cb(null, response)
|
|
57
|
-
else return Promise.resolve(response)
|
|
1
|
+
const util = require('./util.js')
|
|
2
|
+
const rules = require('./rules.js')
|
|
3
|
+
const Model = require('./model.js')
|
|
4
|
+
|
|
5
|
+
Model.prototype.validate = async function (data, opts) {
|
|
6
|
+
/**
|
|
7
|
+
* Validates a model
|
|
8
|
+
* @param {object} data
|
|
9
|
+
* @param {object} <opts>
|
|
10
|
+
* @param {array|string|false} <opts.blacklist> - augment insertBL/updateBL, `false` will remove blacklisting
|
|
11
|
+
* @param {array|string} <opts.project> - return only these fields, ignores blacklisting
|
|
12
|
+
* @param {array|string|true} <opts.skipValidation> - skip validation on these fields
|
|
13
|
+
* @param {boolean} <opts.timestamps> - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
|
|
14
|
+
* updated, depending on the `options.update` value
|
|
15
|
+
* @param {boolean(false)} <opts.update> - are we validating for insert or update? todo: change to `type`
|
|
16
|
+
* @param {array|string|false} <opts.validateUndefined> - validates all 'required' undefined fields, true by
|
|
17
|
+
* default, but false on update
|
|
18
|
+
* @return promise(errors[] || pruned data{})
|
|
19
|
+
* @this model
|
|
20
|
+
*/
|
|
21
|
+
data = util.deepCopy(data)
|
|
22
|
+
opts = opts || {}
|
|
23
|
+
opts.update = opts.update || opts.findOneAndUpdate
|
|
24
|
+
opts.insert = !opts.update
|
|
25
|
+
opts.skipValidation = opts.skipValidation === true ? true : util.toArray(opts.skipValidation||[])
|
|
26
|
+
|
|
27
|
+
// Get projection
|
|
28
|
+
if (opts.project) opts.projectionValidate = this._getProjectionFromProject(opts.project)
|
|
29
|
+
else opts.projectionValidate = this._getProjectionFromBlacklist(opts.update ? 'update' : 'insert', opts.blacklist)
|
|
30
|
+
|
|
31
|
+
// Hook: beforeValidate
|
|
32
|
+
await util.runSeries(this.beforeValidate.map(f => f.bind(opts, data)))
|
|
33
|
+
|
|
34
|
+
// Recurse and validate fields
|
|
35
|
+
let response = util.toArray(data).map(item => {
|
|
36
|
+
let validated = this._validateFields(item, this.fields, item, opts, '')
|
|
37
|
+
if (validated[0].length) throw validated[0]
|
|
38
|
+
else return validated[1]
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Single document?
|
|
42
|
+
response = util.isArray(data)? response : response[0]
|
|
43
|
+
|
|
44
|
+
// Success/error
|
|
45
|
+
return Promise.resolve(response)
|
|
46
|
+
}
|
|
58
47
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
48
|
+
Model.prototype._getMostSpecificKeyMatchingPath = function (object, path) {
|
|
49
|
+
/**
|
|
50
|
+
* Get all possible array variation matches from the object, and return the most specifc key
|
|
51
|
+
* @param {object} object - messages, e.g. { 'pets.1.name', 'pets.$.name', 'pets.name', .. }
|
|
52
|
+
* @path {string} path - must be a specifc path, e.g. 'pets.1.name'
|
|
53
|
+
* @return most specific key in object
|
|
54
|
+
*/
|
|
55
|
+
let key
|
|
56
|
+
for (let k in object) {
|
|
57
|
+
if (path.match(object[k].regex)) {
|
|
58
|
+
key = k
|
|
59
|
+
break
|
|
62
60
|
}
|
|
63
|
-
}
|
|
61
|
+
}
|
|
62
|
+
return key
|
|
63
|
+
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
65
|
+
Model.prototype._validateFields = function (dataRoot, fields, data, opts, path) {
|
|
66
|
+
/**
|
|
67
|
+
* Recurse through and retrieve any errors and valid data
|
|
68
|
+
* @param {any} dataRoot
|
|
69
|
+
* @param {object|array} fields
|
|
70
|
+
* @param {any} data
|
|
71
|
+
* @param {object} opts
|
|
72
|
+
* @param {string} path
|
|
73
|
+
* @return [errors, valid-data]
|
|
74
|
+
* @this model
|
|
75
|
+
*
|
|
76
|
+
* Fields first recursion = { pets: [{ name: {}, color: {} }] }
|
|
77
|
+
* Fields second recursion = [0]: { name: {}, color: {} }
|
|
78
|
+
*/
|
|
79
|
+
let errors = []
|
|
80
|
+
let data2 = util.isArray(fields)? [] : {}
|
|
81
|
+
let timestamps = util.isDefined(opts.timestamps) ? opts.timestamps : this.manager.opts.timestamps
|
|
82
|
+
|
|
83
|
+
util.forEach(util.forceArray(data), function(data, i) {
|
|
84
|
+
util.forEach(fields, function(field, fieldName) {
|
|
85
|
+
let verrors = []
|
|
86
|
+
let schema = field.schema || field
|
|
87
|
+
let value = util.isArray(fields)? data : (data||{})[fieldName]
|
|
88
|
+
let indexOrFieldName = util.isArray(fields)? i : fieldName
|
|
89
|
+
let path2 = `${path}.${indexOrFieldName}`.replace(/^\./, '')
|
|
90
|
+
let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name = pets.name
|
|
91
|
+
let isType = 'is' + util.ucFirst(schema.type)
|
|
92
|
+
let isTypeRule = this.rules[isType] || rules[isType]
|
|
93
|
+
|
|
94
|
+
// Timestamp overrides
|
|
95
|
+
if (schema.timestampField) {
|
|
96
|
+
if (timestamps && ((fieldName == 'createdAt' && opts.insert) || fieldName == 'updatedAt')) {
|
|
97
|
+
value = schema.default.call(dataRoot, fieldName, this)
|
|
98
|
+
}
|
|
99
|
+
// Use the default if available
|
|
100
|
+
} else if (util.isDefined(schema.default)) {
|
|
101
|
+
if ((!util.isDefined(value) && opts.insert) || schema.defaultOverride) {
|
|
102
|
+
value = util.isFunction(schema.default)? schema.default.call(dataRoot, fieldName, this) : schema.default
|
|
103
|
+
}
|
|
77
104
|
}
|
|
78
|
-
}
|
|
79
|
-
return key
|
|
80
|
-
},
|
|
81
105
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
*
|
|
93
|
-
* Fields first recursion = { pets: [{ name: {}, color: {} }] }
|
|
94
|
-
* Fields second recursion = [0]: { name: {}, color: {} }
|
|
95
|
-
*/
|
|
96
|
-
let errors = []
|
|
97
|
-
let data2 = util.isArray(fields)? [] : {}
|
|
98
|
-
let timestamps = util.isDefined(opts.timestamps)? opts.timestamps : this.manager.timestamps
|
|
99
|
-
|
|
100
|
-
util.forEach(util.forceArray(data), function(data, i) {
|
|
101
|
-
util.forEach(fields, function(field, fieldName) {
|
|
102
|
-
let verrors = []
|
|
103
|
-
let schema = field.schema || field
|
|
104
|
-
let value = util.isArray(fields)? data : (data||{})[fieldName]
|
|
105
|
-
let indexOrFieldName = util.isArray(fields)? i : fieldName
|
|
106
|
-
let path2 = `${path}.${indexOrFieldName}`.replace(/^\./, '')
|
|
107
|
-
let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name = pets.name
|
|
108
|
-
let isType = 'is' + util.ucFirst(schema.type)
|
|
109
|
-
let isTypeRule = this.rules[isType] || rules[isType]
|
|
106
|
+
// Ignore blacklisted
|
|
107
|
+
if (this._pathBlacklisted(path3, opts.projectionValidate) && !schema.defaultOverride) return
|
|
108
|
+
// Ignore insert only
|
|
109
|
+
if (opts.update && schema.insertOnly) return
|
|
110
|
+
// Ignore virtual fields
|
|
111
|
+
if (schema.virtual) return
|
|
112
|
+
// Type cast the value if tryParse is available, .e.g. isInteger.tryParse
|
|
113
|
+
if (isTypeRule && util.isFunction(isTypeRule.tryParse)) {
|
|
114
|
+
value = isTypeRule.tryParse.call(dataRoot, value, fieldName, this)
|
|
115
|
+
}
|
|
110
116
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
// Schema field (ignore object/array schemas)
|
|
118
|
+
if (util.isSchema(field) && fieldName !== 'schema') {
|
|
119
|
+
errors.push(...(verrors = this._validateRules(dataRoot, schema, value, opts, path2)))
|
|
120
|
+
if (util.isDefined(value) && !verrors.length) data2[indexOrFieldName] = value
|
|
121
|
+
|
|
122
|
+
// Fields can be a subdocument
|
|
123
|
+
} else if (util.isSubdocument(field)) {
|
|
124
|
+
// Object schema errors
|
|
125
|
+
errors.push(...(verrors = this._validateRules(dataRoot, schema, value, opts, path2)))
|
|
126
|
+
// Recurse if inserting, value is a subdocument, or a deep property (todo: not dot-notation)
|
|
127
|
+
if (
|
|
128
|
+
opts.insert ||
|
|
129
|
+
util.isObject(value) ||
|
|
130
|
+
(util.isDefined(opts.validateUndefined) ? opts.validateUndefined : (path2||'').match(/\./))
|
|
131
|
+
) {
|
|
132
|
+
var res = this._validateFields(dataRoot, field, value, opts, path2)
|
|
133
|
+
errors.push(...res[0])
|
|
121
134
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (this._pathBlacklisted(path3, opts.projectionValidate) && !schema.defaultOverride) return
|
|
125
|
-
// Ignore insert only
|
|
126
|
-
if (opts.update && schema.insertOnly) return
|
|
127
|
-
// Ignore virtual fields
|
|
128
|
-
if (schema.virtual) return
|
|
129
|
-
// Type cast the value if tryParse is available, .e.g. isInteger.tryParse
|
|
130
|
-
if (isTypeRule && util.isFunction(isTypeRule.tryParse)) {
|
|
131
|
-
value = isTypeRule.tryParse.call(dataRoot, value, fieldName, this)
|
|
135
|
+
if (util.isDefined(value) && !verrors.length) {
|
|
136
|
+
data2[indexOrFieldName] = res? res[1] : value
|
|
132
137
|
}
|
|
133
138
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
opts.insert ||
|
|
146
|
-
util.isObject(value) ||
|
|
147
|
-
(util.isDefined(opts.validateUndefined) ? opts.validateUndefined : (path2||'').match(/\./))
|
|
148
|
-
) {
|
|
149
|
-
var res = this._validateFields(dataRoot, field, value, opts, path2)
|
|
150
|
-
errors.push(...res[0])
|
|
151
|
-
}
|
|
152
|
-
if (util.isDefined(value) && !verrors.length) {
|
|
153
|
-
data2[indexOrFieldName] = res? res[1] : value
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Fields can be an array
|
|
157
|
-
} else if (util.isArray(field)) {
|
|
158
|
-
// Array schema errors
|
|
159
|
-
errors.push(...(verrors = this._validateRules(dataRoot, schema, value, opts, path2)))
|
|
160
|
-
// Data value is array too
|
|
161
|
-
if (util.isArray(value)) {
|
|
162
|
-
var res2 = this._validateFields(dataRoot, field, value, opts, path2)
|
|
163
|
-
errors.push(...res2[0])
|
|
164
|
-
}
|
|
165
|
-
if (util.isDefined(value) && !verrors.length) {
|
|
166
|
-
data2[indexOrFieldName] = res2? res2[1] : value
|
|
167
|
-
}
|
|
139
|
+
// Fields can be an array
|
|
140
|
+
} else if (util.isArray(field)) {
|
|
141
|
+
// Array schema errors
|
|
142
|
+
errors.push(...(verrors = this._validateRules(dataRoot, schema, value, opts, path2)))
|
|
143
|
+
// Data value is array too
|
|
144
|
+
if (util.isArray(value)) {
|
|
145
|
+
var res2 = this._validateFields(dataRoot, field, value, opts, path2)
|
|
146
|
+
errors.push(...res2[0])
|
|
147
|
+
}
|
|
148
|
+
if (util.isDefined(value) && !verrors.length) {
|
|
149
|
+
data2[indexOrFieldName] = res2? res2[1] : value
|
|
168
150
|
}
|
|
169
|
-
}
|
|
151
|
+
}
|
|
170
152
|
}, this)
|
|
153
|
+
}, this)
|
|
171
154
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
_validateRules: function(dataRoot, field, value, opts, path) {
|
|
179
|
-
/**
|
|
180
|
-
* Validate all the field's rules
|
|
181
|
-
* @param {object} dataRoot - data
|
|
182
|
-
* @param {object} field - field schema
|
|
183
|
-
* @param {string} path - full field path
|
|
184
|
-
* @param {object} opts - original validate() options
|
|
185
|
-
* @return {array} errors
|
|
186
|
-
* @this model
|
|
187
|
-
*/
|
|
188
|
-
let errors = []
|
|
189
|
-
if (opts.skipValidation === true) return []
|
|
155
|
+
// Normalise array indexes and return
|
|
156
|
+
if (util.isArray(fields)) data2 = data2.filter(() => true)
|
|
157
|
+
if (data === null) data2 = null
|
|
158
|
+
return [errors, data2]
|
|
159
|
+
}
|
|
190
160
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
161
|
+
Model.prototype._validateRules = function (dataRoot, field, value, opts, path) {
|
|
162
|
+
/**
|
|
163
|
+
* Validate all the field's rules
|
|
164
|
+
* @param {object} dataRoot - data
|
|
165
|
+
* @param {object} field - field schema
|
|
166
|
+
* @param {string} path - full field path
|
|
167
|
+
* @param {object} opts - original validate() options
|
|
168
|
+
* @return {array} errors
|
|
169
|
+
* @this model
|
|
170
|
+
*/
|
|
171
|
+
let errors = []
|
|
172
|
+
if (opts.skipValidation === true) return []
|
|
173
|
+
|
|
174
|
+
// Skip validation for a field, takes in to account if a parent has been skipped.
|
|
175
|
+
if (opts.skipValidation.length) {
|
|
176
|
+
//console.log(path, field, opts)
|
|
177
|
+
let pathChunks = path.split('.')
|
|
178
|
+
for (let skippedField of opts.skipValidation) {
|
|
179
|
+
// Make sure there is numerical character representing arrays
|
|
180
|
+
let skippedFieldChunks = skippedField.split('.')
|
|
181
|
+
for (let i=0, l=pathChunks.length; i<l; i++) {
|
|
182
|
+
if (pathChunks[i].match(/^[0-9]+$/)
|
|
183
|
+
&& skippedFieldChunks[i]
|
|
184
|
+
&& !skippedFieldChunks[i].match(/^\$$|^[0-9]+$/)) {
|
|
185
|
+
skippedFieldChunks.splice(i, 0, '$')
|
|
204
186
|
}
|
|
205
|
-
for (let i=0, l=skippedFieldChunks.length; i<l; i++) {
|
|
206
|
-
if (skippedFieldChunks[i] == '$') skippedFieldChunks[i] = '[0-9]+'
|
|
207
|
-
}
|
|
208
|
-
if (path.match(new RegExp('^' + skippedFieldChunks.join('.') + '(.|$)'))) return []
|
|
209
187
|
}
|
|
188
|
+
for (let i=0, l=skippedFieldChunks.length; i<l; i++) {
|
|
189
|
+
if (skippedFieldChunks[i] == '$') skippedFieldChunks[i] = '[0-9]+'
|
|
190
|
+
}
|
|
191
|
+
if (path.match(new RegExp('^' + skippedFieldChunks.join('.') + '(.|$)'))) return []
|
|
210
192
|
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
for (let ruleName in field) {
|
|
196
|
+
if (this._ignoredRules.indexOf(ruleName) > -1) continue
|
|
197
|
+
let error = this._validateRule(dataRoot, ruleName, field, field[ruleName], value, opts, path)
|
|
198
|
+
if (error && ruleName == 'required') return [error] // only show the required error
|
|
199
|
+
if (error) errors.push(error)
|
|
200
|
+
}
|
|
201
|
+
return errors
|
|
202
|
+
}
|
|
211
203
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
// Rule failed
|
|
246
|
-
if (!rule.fn.call(dataRoot, value, ruleArg, path, this)) return {
|
|
247
|
-
detail: util.isFunction(ruleMessage)
|
|
248
|
-
? ruleMessage.call(dataRoot, value, ruleArg, path, this)
|
|
249
|
-
: ruleMessage,
|
|
250
|
-
meta: { rule: ruleName, model: this.name, field: fieldName, detailLong: rule.messageLong },
|
|
251
|
-
status: '400',
|
|
252
|
-
title: path
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
|
|
256
|
-
_ignoredRules: [ // todo: change name? i.e. 'specialFields'
|
|
257
|
-
// Need to remove filesize and formats..
|
|
258
|
-
'awsAcl', 'awsBucket', 'default', 'defaultOverride', 'filename', 'filesize', 'fileSize', 'formats',
|
|
259
|
-
'image', 'index', 'insertOnly', 'model', 'nullObject', 'params', 'path', 'getSignedUrl', 'timestampField',
|
|
260
|
-
'type', 'virtual',
|
|
261
|
-
]
|
|
262
|
-
|
|
204
|
+
Model.prototype._validateRule = function (dataRoot, ruleName, field, ruleArg, value, opts, path) {
|
|
205
|
+
// this.debug(path, field, ruleName, ruleArg, value)
|
|
206
|
+
// Remove [] from the message path, and simply ignore non-numeric children to test for all array items
|
|
207
|
+
ruleArg = ruleArg === true? undefined : ruleArg
|
|
208
|
+
let rule = this.rules[ruleName] || rules[ruleName]
|
|
209
|
+
let fieldName = path.match(/[^.]+$/)[0]
|
|
210
|
+
let isDeepProp = path.match(/\./) // todo: not dot-notation
|
|
211
|
+
let ruleMessageKey = this.messagesLen && this._getMostSpecificKeyMatchingPath(this.messages, path)
|
|
212
|
+
let ruleMessage = ruleMessageKey && this.messages[ruleMessageKey][ruleName]
|
|
213
|
+
let validateUndefined = util.isDefined(opts.validateUndefined) ? opts.validateUndefined : opts.insert || isDeepProp
|
|
214
|
+
if (!ruleMessage) ruleMessage = rule.message
|
|
215
|
+
|
|
216
|
+
// Undefined value
|
|
217
|
+
if (typeof value === 'undefined' && (!validateUndefined || !rule.validateUndefined)) return
|
|
218
|
+
|
|
219
|
+
// Ignore null (if nullObject is set on objects or arrays)
|
|
220
|
+
if (value === null && (field.isObject || field.isArray) && field.nullObject && !rule.validateNull) return
|
|
221
|
+
|
|
222
|
+
// Ignore null
|
|
223
|
+
if (value === null && !(field.isObject || field.isArray) && !rule.validateNull) return
|
|
224
|
+
|
|
225
|
+
// Ignore empty strings
|
|
226
|
+
if (value === '' && !rule.validateEmptyString) return
|
|
227
|
+
|
|
228
|
+
// Rule failed
|
|
229
|
+
if (!rule.fn.call(dataRoot, value, ruleArg, path, this)) return {
|
|
230
|
+
detail: util.isFunction(ruleMessage)
|
|
231
|
+
? ruleMessage.call(dataRoot, value, ruleArg, path, this)
|
|
232
|
+
: ruleMessage,
|
|
233
|
+
meta: { rule: ruleName, model: this.name, field: fieldName, detailLong: rule.messageLong },
|
|
234
|
+
status: '400',
|
|
235
|
+
title: path,
|
|
236
|
+
}
|
|
263
237
|
}
|
|
238
|
+
|
|
239
|
+
Model.prototype._ignoredRules = [
|
|
240
|
+
// todo: change name? i.e. 'specialFields'
|
|
241
|
+
// todo: need to remove filesize and formats..
|
|
242
|
+
'awsAcl', 'awsBucket', 'default', 'defaultOverride', 'filename', 'filesize', 'fileSize', 'formats',
|
|
243
|
+
'image', 'index', 'insertOnly', 'model', 'nullObject', 'params', 'path', 'getSignedUrl', 'timestampField',
|
|
244
|
+
'type', 'virtual',
|
|
245
|
+
]
|