monastery 3.0.23 → 3.1.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/changelog.md +4 -0
- package/lib/model-crud.js +6 -3
- package/lib/model-validate.js +14 -2
- package/lib/model.js +41 -16
- package/package.json +1 -1
- package/test/model.js +59 -55
- package/test/validate.js +81 -0
package/changelog.md
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [3.1.1](https://github.com/boycce/monastery/compare/3.1.0...3.1.1) (2024-05-27)
|
|
6
|
+
|
|
7
|
+
## [3.1.0](https://github.com/boycce/monastery/compare/3.0.23...3.1.0) (2024-05-27)
|
|
8
|
+
|
|
5
9
|
### [3.0.23](https://github.com/boycce/monastery/compare/3.0.22...3.0.23) (2024-05-25)
|
|
6
10
|
|
|
7
11
|
### [3.0.22](https://github.com/boycce/monastery/compare/3.0.21...3.0.22) (2024-05-08)
|
package/lib/model-crud.js
CHANGED
|
@@ -33,7 +33,8 @@ Model.prototype.insert = async function (opts) {
|
|
|
33
33
|
* @param {array|string|false} <opts.blacklist> - augment schema.insertBL, `false` will remove blacklisting
|
|
34
34
|
* @param {array|string} <opts.project> - return only these fields, ignores blacklisting
|
|
35
35
|
* @param {boolean} <opts.respond> - automatically call res.json/error (requires opts.req)
|
|
36
|
-
* @param {array|string|true} <opts.skipValidation> - skip validation
|
|
36
|
+
* @param {array|string|true} <opts.skipValidation> - skip validation on these fields, or pass `true` to skip
|
|
37
|
+
* all fields and hooks
|
|
37
38
|
* @param {boolean} <opts.timestamps> - whether `createdAt` and `updatedAt` are automatically inserted
|
|
38
39
|
* @param {array|string|false} <opts.validateUndefined> - validates all 'required' undefined fields, true by
|
|
39
40
|
* default, but false on update
|
|
@@ -223,7 +224,8 @@ Model.prototype.findOneAndUpdate = async function (opts) {
|
|
|
223
224
|
*
|
|
224
225
|
* Update options:
|
|
225
226
|
* @param {object|array} opts.data - mongodb document update object(s)
|
|
226
|
-
* @param {array|string|true} <opts.skipValidation
|
|
227
|
+
* @param {array|string|true} <opts.skipValidation>- skip validation on these fields, or pass `true` to skip
|
|
228
|
+
* all fields and hooks
|
|
227
229
|
* @param {boolean} <opts.timestamps> - whether `updatedAt` is automatically updated
|
|
228
230
|
* @param {array|string|false} <opts.validateUndefined> - validates all 'required' undefined fields, true by
|
|
229
231
|
* default, but false on update
|
|
@@ -261,7 +263,8 @@ Model.prototype.update = async function (opts, type='update') {
|
|
|
261
263
|
* @param {array|string} <opts.project> - return only these fields, ignores blacklisting
|
|
262
264
|
* @param {object} <opts.query> - mongodb query object
|
|
263
265
|
* @param {boolean} <opts.respond> - automatically call res.json/error (requires opts.req)
|
|
264
|
-
* @param {array|string|true} <opts.skipValidation> - skip validation
|
|
266
|
+
* @param {array|string|true} <opts.skipValidation> - skip validation on these fields, or pass `true` to skip
|
|
267
|
+
* all fields and hooks
|
|
265
268
|
* @param {boolean} <opts.timestamps> - whether `updatedAt` is automatically updated
|
|
266
269
|
* @param {array|string|false} <opts.validateUndefined> - validates all 'required' undefined fields, true by
|
|
267
270
|
* default, but false on update
|
package/lib/model-validate.js
CHANGED
|
@@ -9,7 +9,8 @@ Model.prototype.validate = async function (data, opts) {
|
|
|
9
9
|
* @param {object} <opts>
|
|
10
10
|
* @param {array|string|false} <opts.blacklist> - augment insertBL/updateBL, `false` will remove blacklisting
|
|
11
11
|
* @param {array|string} <opts.project> - return only these fields, ignores blacklisting
|
|
12
|
-
* @param {array|string|true} <opts.skipValidation> - skip validation on these fields
|
|
12
|
+
* @param {array|string|true} <opts.skipValidation> - skip validation on these fields, or pass `true` to skip
|
|
13
|
+
* all fields and hooks
|
|
13
14
|
* @param {boolean} <opts.timestamps> - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
|
|
14
15
|
* updated, depending on the `options.update` value
|
|
15
16
|
* @param {boolean(false)} <opts.update> - are we validating for insert or update? todo: change to `type`
|
|
@@ -25,6 +26,7 @@ Model.prototype.validate = async function (data, opts) {
|
|
|
25
26
|
opts.update = opts.update || opts.findOneAndUpdate
|
|
26
27
|
opts.insert = !opts.update
|
|
27
28
|
opts.skipValidation = opts.skipValidation === true ? true : util.toArray(opts.skipValidation||[])
|
|
29
|
+
if (opts.skipValidation === true) return data
|
|
28
30
|
|
|
29
31
|
// Get projection
|
|
30
32
|
if (opts.project) var projectionValidate = this._getProjectionFromProject(opts.project)
|
|
@@ -91,6 +93,7 @@ Model.prototype._validateFields = function (dataRoot, fields, data, opts, parent
|
|
|
91
93
|
let timestamps = util.isDefined(opts.timestamps) ? opts.timestamps : this.manager.opts.timestamps
|
|
92
94
|
let dataArray = util.forceArray(data)
|
|
93
95
|
let data2 = fieldsIsArray ? [] : {}
|
|
96
|
+
let notStrict = fields.schema.strict === false
|
|
94
97
|
|
|
95
98
|
for (let i=0, l=dataArray.length; i<l; i++) {
|
|
96
99
|
const item = dataArray[i]
|
|
@@ -172,6 +175,15 @@ Model.prototype._validateFields = function (dataRoot, fields, data, opts, parent
|
|
|
172
175
|
}
|
|
173
176
|
// if (!parentPath && fieldName == 'categories') console.timeEnd(fieldName)
|
|
174
177
|
}
|
|
178
|
+
|
|
179
|
+
// Add any extra fields that are not in the schema. Item maybe false when inserting (from recursing above)
|
|
180
|
+
if (notStrict && !fieldsIsArray && item) {
|
|
181
|
+
const allDataKeys = Object.keys(item)
|
|
182
|
+
for (let m=0, n=allDataKeys.length; m<n; m++) {
|
|
183
|
+
const key = allDataKeys[m]
|
|
184
|
+
if (!fieldsArray.includes(key)) data2[key] = item[key]
|
|
185
|
+
}
|
|
186
|
+
}
|
|
175
187
|
}
|
|
176
188
|
|
|
177
189
|
// Normalise array indexes and return
|
|
@@ -266,5 +278,5 @@ Model.prototype._ignoredRules = [
|
|
|
266
278
|
// todo: need to remove filesize and formats..
|
|
267
279
|
'awsAcl', 'awsBucket', 'default', 'defaultOverride', 'filename', 'filesize', 'fileSize', 'formats',
|
|
268
280
|
'image', 'index', 'insertOnly', 'model', 'nullObject', 'params', 'path', 'getSignedUrl', 'timestampField',
|
|
269
|
-
'type', 'isType', 'isSchema', 'virtual',
|
|
281
|
+
'type', 'isType', 'isSchema', 'virtual', 'strict',
|
|
270
282
|
]
|
package/lib/model.js
CHANGED
|
@@ -21,6 +21,11 @@ function Model(name, opts, manager) {
|
|
|
21
21
|
} else if (!opts.fields) {
|
|
22
22
|
throw `We couldn't find ${name}.fields in the model definition, the model maybe setup `
|
|
23
23
|
+ `or exported incorrectly:\n${JSON.stringify(opts, null, 2)}`
|
|
24
|
+
} else if (!util.isSubdocument(opts.fields) && opts.fields.type == 'any') {
|
|
25
|
+
throw `Instead of using { type: 'any' } for ${name}.fields, please use the new 'strict' definition rule` +
|
|
26
|
+
', e.g. { schema: { strict: false }}'
|
|
27
|
+
} else if (!util.isSubdocument(opts.fields) && !util.isEmpty(opts.fields)) {
|
|
28
|
+
throw `The ${name}.fields object should be a valid document, e.g. { name: { type: 'string' }}`
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
// Add schema options
|
|
@@ -107,7 +112,9 @@ function Model(name, opts, manager) {
|
|
|
107
112
|
}, this)
|
|
108
113
|
|
|
109
114
|
// Extend default fields with passed in fields and check for invalid fields
|
|
110
|
-
this._setupFields(
|
|
115
|
+
this._setupFields(
|
|
116
|
+
this.fields = util.isSchema(this.fields) ? this.fields : Object.assign({}, this._defaultFields, this.fields)
|
|
117
|
+
)
|
|
111
118
|
this.fieldsFlattened = this._getFieldsFlattened(this.fields, '') // test output?
|
|
112
119
|
this.modelFieldsArray = this._getModelFieldsArray()
|
|
113
120
|
|
|
@@ -173,11 +180,14 @@ Model.prototype._getModelFieldsArray = function() {
|
|
|
173
180
|
}, [])
|
|
174
181
|
},
|
|
175
182
|
|
|
176
|
-
Model.prototype._setupFields = function(fields) {
|
|
183
|
+
Model.prototype._setupFields = function(fields, isSub) {
|
|
177
184
|
/**
|
|
178
185
|
* Check for invalid rules on a field object, and set field.isType
|
|
179
186
|
* @param {object|array} fields - subsdocument or array
|
|
180
187
|
*/
|
|
188
|
+
// We need to allow the processing of the root schema object
|
|
189
|
+
if (!isSub) fields = { fields }
|
|
190
|
+
|
|
181
191
|
util.forEach(fields, function(field, fieldName) {
|
|
182
192
|
// Schema field
|
|
183
193
|
if (fieldName == 'schema') return
|
|
@@ -214,17 +224,8 @@ Model.prototype._setupFields = function(fields) {
|
|
|
214
224
|
isSchema: true,
|
|
215
225
|
}
|
|
216
226
|
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
if ((this.rules[ruleName] || rules[ruleName]) && this._ignoredRules.indexOf(ruleName) != -1) {
|
|
220
|
-
this.error(`The rule name "${ruleName}" for the model "${this.name}" is a reserved keyword, ignoring rule.`)
|
|
221
|
-
}
|
|
222
|
-
if (!this.rules[ruleName] && !rules[ruleName] && this._ignoredRules.indexOf(ruleName) == -1) {
|
|
223
|
-
// console.log(field.schema)
|
|
224
|
-
this.error(`No rule "${ruleName}" exists for model "${this.name}", ignoring rule.`)
|
|
225
|
-
delete field.schema[ruleName]
|
|
226
|
-
}
|
|
227
|
-
}
|
|
227
|
+
// Remove invalid rules
|
|
228
|
+
this._removeInvalidRules(field)
|
|
228
229
|
|
|
229
230
|
// Misused schema property
|
|
230
231
|
} else if (fieldName == 'schema' || fieldName == 'isSchema') {
|
|
@@ -233,6 +234,7 @@ Model.prototype._setupFields = function(fields) {
|
|
|
233
234
|
|
|
234
235
|
// Fields be an array
|
|
235
236
|
} else if (util.isArray(field)) {
|
|
237
|
+
this._removeInvalidRules(field)
|
|
236
238
|
field.schema = util.removeUndefined({
|
|
237
239
|
type: 'array',
|
|
238
240
|
isArray: true,
|
|
@@ -243,7 +245,7 @@ Model.prototype._setupFields = function(fields) {
|
|
|
243
245
|
virtual: field.length == 1 && (field[0] || {}).virtual ? true : undefined,
|
|
244
246
|
...(field.schema || {}),
|
|
245
247
|
})
|
|
246
|
-
this._setupFields(field)
|
|
248
|
+
this._setupFields(field, true)
|
|
247
249
|
|
|
248
250
|
// Fields can be a subdocument, e.g. user.pet = { name: {}, ..}
|
|
249
251
|
} else if (util.isSubdocument(field)) {
|
|
@@ -253,6 +255,7 @@ Model.prototype._setupFields = function(fields) {
|
|
|
253
255
|
field.schema.index = index2dsphere
|
|
254
256
|
delete field.index
|
|
255
257
|
}
|
|
258
|
+
this._removeInvalidRules(field)
|
|
256
259
|
field.schema = util.removeUndefined({
|
|
257
260
|
type: 'object',
|
|
258
261
|
isObject: true,
|
|
@@ -262,11 +265,33 @@ Model.prototype._setupFields = function(fields) {
|
|
|
262
265
|
nullObject: this.manager.opts.nullObjects,
|
|
263
266
|
...field.schema,
|
|
264
267
|
})
|
|
265
|
-
this._setupFields(field)
|
|
268
|
+
this._setupFields(field, true)
|
|
266
269
|
}
|
|
267
270
|
}, this)
|
|
268
271
|
},
|
|
269
272
|
|
|
273
|
+
Model.prototype._removeInvalidRules = function(field) {
|
|
274
|
+
/**
|
|
275
|
+
* Remove invalid rules on a field object
|
|
276
|
+
* @param {object} field
|
|
277
|
+
* @return {object} field
|
|
278
|
+
**/
|
|
279
|
+
for (let ruleName in (field||{}).schema) {
|
|
280
|
+
const ruleFn = this.rules[ruleName] || rules[ruleName]
|
|
281
|
+
// Rule doesn't exist
|
|
282
|
+
if (!ruleFn && this._ignoredRules.indexOf(ruleName) == -1) {
|
|
283
|
+
// console.log(field.schema)
|
|
284
|
+
this.error(`No rule "${ruleName}" exists for model "${this.name}", ignoring rule.`)
|
|
285
|
+
delete field.schema[ruleName]
|
|
286
|
+
}
|
|
287
|
+
// Reserved rule
|
|
288
|
+
if (this.rules[ruleName] && this._ignoredRules.indexOf(ruleName) != -1) {
|
|
289
|
+
this.error(`The rule "${ruleName}" for the model "${this.name}" is a reserved keyword, ignoring custom rule function.`)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return field
|
|
293
|
+
},
|
|
294
|
+
|
|
270
295
|
Model.prototype._setupIndexes = async function(fields, opts={}) {
|
|
271
296
|
/**
|
|
272
297
|
* Creates indexes for the model (multikey, and sub-document supported)
|
|
@@ -294,7 +319,7 @@ Model.prototype._setupIndexes = async function(fields, opts={}) {
|
|
|
294
319
|
}
|
|
295
320
|
|
|
296
321
|
// Process custom 'unprocessed' fields
|
|
297
|
-
if (fields && !fields[Object.keys(fields)[0]].schema) {
|
|
322
|
+
if (fields && !(fields[Object.keys(fields)[0]].schema||{}).isSchema) {
|
|
298
323
|
fields = util.deepCopy(fields)
|
|
299
324
|
this._setupFields(fields)
|
|
300
325
|
// console.dir(fields, { depth: null })
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "monastery",
|
|
3
3
|
"description": "⛪ A simple, straightforward MongoDB ODM",
|
|
4
4
|
"author": "Ricky Boyce",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.1.1",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/test/model.js
CHANGED
|
@@ -21,18 +21,8 @@ test('model > model on manager', async () => {
|
|
|
21
21
|
db2.close()
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
-
test('model setup
|
|
25
|
-
//
|
|
26
|
-
let user = db.model('user', { fields: {
|
|
27
|
-
name: { type: 'string' },
|
|
28
|
-
pets: [{ type: 'string' }],
|
|
29
|
-
colors: { red: { type: 'string' } },
|
|
30
|
-
points: [[{ type: 'number' }]],
|
|
31
|
-
points2: [[{ x: { type: 'number' } }]],
|
|
32
|
-
logo: { type: 'image' },
|
|
33
|
-
}})
|
|
34
|
-
|
|
35
|
-
// no fields defined
|
|
24
|
+
test('model setup with default fields', async () => {
|
|
25
|
+
// Default fields
|
|
36
26
|
expect(db.model('user2', { fields: {} }).fields).toEqual({
|
|
37
27
|
_id: {
|
|
38
28
|
schema: {
|
|
@@ -64,12 +54,39 @@ test('model setup basics', async () => {
|
|
|
64
54
|
type: 'integer',
|
|
65
55
|
},
|
|
66
56
|
},
|
|
57
|
+
schema: {
|
|
58
|
+
isObject: true,
|
|
59
|
+
isSchema: true,
|
|
60
|
+
isType: 'isObject',
|
|
61
|
+
type: 'object',
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('model setup basics', async () => {
|
|
67
|
+
// Setup
|
|
68
|
+
let user = db.model('user', {
|
|
69
|
+
fields: {
|
|
70
|
+
name: { type: 'string' },
|
|
71
|
+
pets: [{ type: 'string' }],
|
|
72
|
+
colors: { red: { type: 'string' } },
|
|
73
|
+
points: [[{ type: 'number' }]],
|
|
74
|
+
points2: [[{ x: { type: 'number' } }]],
|
|
75
|
+
logo: { type: 'image' },
|
|
76
|
+
},
|
|
67
77
|
})
|
|
68
78
|
|
|
69
79
|
// Has model name
|
|
70
80
|
expect(user.name)
|
|
71
81
|
.toEqual('user')
|
|
72
82
|
|
|
83
|
+
// Expect to throw an error
|
|
84
|
+
expect(() => db.model('user', { fields: { type: 'any' } }))
|
|
85
|
+
.toThrow(
|
|
86
|
+
'Instead of using { type: \'any\' } for user.fields, please use the new \'strict\' definition rule, '
|
|
87
|
+
+ 'e.g. { schema: { strict: false }}'
|
|
88
|
+
)
|
|
89
|
+
|
|
73
90
|
// Basic field
|
|
74
91
|
expect(user.fields.name.schema)
|
|
75
92
|
.toEqual({ type: 'string', isString: true, isSchema: true, isType: 'isString' })
|
|
@@ -114,42 +131,6 @@ test('model setup basics', async () => {
|
|
|
114
131
|
))
|
|
115
132
|
})
|
|
116
133
|
|
|
117
|
-
test('model setup with default fields', async () => {
|
|
118
|
-
// Default fields
|
|
119
|
-
expect(db.model('user2', { fields: {} }).fields).toEqual({
|
|
120
|
-
_id: {
|
|
121
|
-
schema: {
|
|
122
|
-
insertOnly: true,
|
|
123
|
-
isId: true,
|
|
124
|
-
isSchema: true,
|
|
125
|
-
isType: 'isId',
|
|
126
|
-
type: 'id',
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
createdAt: {
|
|
130
|
-
schema: {
|
|
131
|
-
default: expect.any(Function),
|
|
132
|
-
insertOnly: true,
|
|
133
|
-
isInteger: true,
|
|
134
|
-
isSchema: true,
|
|
135
|
-
isType: 'isInteger',
|
|
136
|
-
timestampField: true,
|
|
137
|
-
type: 'integer',
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
updatedAt: {
|
|
141
|
-
schema: {
|
|
142
|
-
default: expect.any(Function),
|
|
143
|
-
isInteger: true,
|
|
144
|
-
isSchema: true,
|
|
145
|
-
isType: 'isInteger',
|
|
146
|
-
timestampField: true,
|
|
147
|
-
type: 'integer',
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
|
|
153
134
|
test('model setup with default objects', async () => {
|
|
154
135
|
const db2 = monastery('127.0.0.1/monastery', { defaultObjects: true })
|
|
155
136
|
let user = db2.model('user', { fields: {
|
|
@@ -248,6 +229,24 @@ test('model setup with schema', async () => {
|
|
|
248
229
|
})
|
|
249
230
|
})
|
|
250
231
|
|
|
232
|
+
test('model setup with schema on root', async () => {
|
|
233
|
+
// Expect to throw an error
|
|
234
|
+
expect(() => db.model('user', { fields: { name: 'string' } }))
|
|
235
|
+
.toThrow('The user.fields object should be a valid document, e.g. { name: { type: \'string\' }}')
|
|
236
|
+
|
|
237
|
+
// root has schema
|
|
238
|
+
expect(db.model('user', { fields: { name: { type: 'string' } } }).fields.schema)
|
|
239
|
+
.toEqual({ type: 'object', isObject: true, isSchema: true, isType: 'isObject' })
|
|
240
|
+
|
|
241
|
+
// root has custom schema
|
|
242
|
+
expect(db.model('user', { fields: { schema: { nullObject: true } } }).fields.schema)
|
|
243
|
+
.toEqual({ type: 'object', isObject: true, isSchema: true, isType: 'isObject', nullObject: true })
|
|
244
|
+
|
|
245
|
+
// strict mode off
|
|
246
|
+
expect(db.model('user', { fields: { schema: { strict: false } } }).fields.schema)
|
|
247
|
+
.toEqual({ type: 'object', isObject: true, isSchema: true, isType: 'isObject', strict: false })
|
|
248
|
+
})
|
|
249
|
+
|
|
251
250
|
test('model setup with messages', async () => {
|
|
252
251
|
let user = db.model('user', {
|
|
253
252
|
fields: {
|
|
@@ -375,15 +374,15 @@ test('model setup with messages', async () => {
|
|
|
375
374
|
})
|
|
376
375
|
})
|
|
377
376
|
|
|
378
|
-
test('model setup reserved rules', async () => {
|
|
377
|
+
test('model setup with reserved and invalid rules', async () => {
|
|
379
378
|
// Setup
|
|
380
379
|
const db2 = monastery('127.0.0.1/monastery', { logLevel: 0 })
|
|
381
380
|
let user = db2.model('user-model', {
|
|
382
381
|
fields: {
|
|
383
382
|
name: {
|
|
384
383
|
type: 'string',
|
|
385
|
-
params: {}, // reserved keyword (image plugin)
|
|
386
|
-
|
|
384
|
+
params: {}, // reserved keyword (image plugin)
|
|
385
|
+
invalidRule: {}, // no rule function found
|
|
387
386
|
},
|
|
388
387
|
},
|
|
389
388
|
rules: {
|
|
@@ -392,10 +391,15 @@ test('model setup reserved rules', async () => {
|
|
|
392
391
|
},
|
|
393
392
|
},
|
|
394
393
|
})
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
394
|
+
expect(user.fields.name).toEqual({
|
|
395
|
+
schema: {
|
|
396
|
+
type: 'string',
|
|
397
|
+
isString: true,
|
|
398
|
+
isSchema: true,
|
|
399
|
+
isType: 'isString',
|
|
400
|
+
params: {}, // still included
|
|
401
|
+
// invalidRule: {}, should be removed
|
|
402
|
+
},
|
|
399
403
|
})
|
|
400
404
|
db2.close()
|
|
401
405
|
})
|
package/test/validate.js
CHANGED
|
@@ -154,6 +154,87 @@ test('validation basic errors', async () => {
|
|
|
154
154
|
})
|
|
155
155
|
})
|
|
156
156
|
|
|
157
|
+
test('validation type any', async () => {
|
|
158
|
+
let user1 = db.model('user', {
|
|
159
|
+
fields: {
|
|
160
|
+
name: { type: 'any' },
|
|
161
|
+
},
|
|
162
|
+
})
|
|
163
|
+
// Any type on field
|
|
164
|
+
await expect(user1.validate({ name: 'benjamin' })).resolves.toEqual({ name: 'benjamin' })
|
|
165
|
+
await expect(user1.validate({ name: 1 })).resolves.toEqual({ name: 1 })
|
|
166
|
+
await expect(user1.validate({ name: null })).resolves.toEqual({ name: null })
|
|
167
|
+
await expect(user1.validate({ name: true })).resolves.toEqual({ name: true })
|
|
168
|
+
await expect(user1.validate({ name: false })).resolves.toEqual({ name: false })
|
|
169
|
+
await expect(user1.validate({ name: [1, 2] })).resolves.toEqual({ name: [1, 2] })
|
|
170
|
+
await expect(user1.validate({ name: { first: 1 } })).resolves.toEqual({ name: { first: 1 } })
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
test('validation schema with reserved and invalid rules', async () => {
|
|
174
|
+
const db2 = monastery('127.0.0.1/monastery', { logLevel: 0 })
|
|
175
|
+
let user = db2.model('user-model', {
|
|
176
|
+
fields: {
|
|
177
|
+
sub: {
|
|
178
|
+
name: {
|
|
179
|
+
type: 'string',
|
|
180
|
+
default: true, // reserved keyword
|
|
181
|
+
invalidRule: {}, // no rule function found
|
|
182
|
+
validRule: true,
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
rules: {
|
|
187
|
+
default: (value) => { // function shouldn't run (i.e. value still is 'Martin')
|
|
188
|
+
return false
|
|
189
|
+
},
|
|
190
|
+
validRule: (value) => {
|
|
191
|
+
if (value === 'Martin') return true
|
|
192
|
+
},
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
await expect(user.validate({ sub: { name: 'Martin' } })).resolves.toEqual({
|
|
196
|
+
createdAt: expect.any(Number),
|
|
197
|
+
updatedAt: expect.any(Number),
|
|
198
|
+
sub: {
|
|
199
|
+
name: 'Martin',
|
|
200
|
+
},
|
|
201
|
+
})
|
|
202
|
+
db2.close()
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
test('validation strict false', async () => {
|
|
206
|
+
let user1 = db.model('user', {
|
|
207
|
+
fields: {
|
|
208
|
+
name: { type: 'string' },
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
// strict on (default)
|
|
212
|
+
let inserted1 = await user1.insert({ data: { nonDefinedField: 1 } })
|
|
213
|
+
expect(inserted1).toEqual({
|
|
214
|
+
_id: inserted1._id,
|
|
215
|
+
})
|
|
216
|
+
let user2 = db.model('user', {
|
|
217
|
+
fields: {
|
|
218
|
+
name: { type: 'string' },
|
|
219
|
+
sub: {
|
|
220
|
+
name: { type: 'string' },
|
|
221
|
+
schema: { strict: false },
|
|
222
|
+
},
|
|
223
|
+
subArray: [{
|
|
224
|
+
name: { type: 'string' },
|
|
225
|
+
schema: { strict: false },
|
|
226
|
+
}],
|
|
227
|
+
schema: { strict: false },
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
// strict off
|
|
231
|
+
let inserted2 = await user2.insert({ data: { nonDefinedField: 1 } })
|
|
232
|
+
expect(inserted2).toEqual({
|
|
233
|
+
_id: inserted2._id,
|
|
234
|
+
nonDefinedField: 1,
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
157
238
|
test('validation subdocument errors', async () => {
|
|
158
239
|
let user = db.model('user', { fields: {
|
|
159
240
|
animals: {
|