monastery 1.33.0 → 1.36.0
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 +1 -1
- package/changelog.md +29 -0
- package/docs/readme.md +5 -2
- package/docs/schema/index.md +3 -3
- package/lib/index.js +37 -32
- package/lib/model-crud.js +189 -113
- package/lib/model-validate.js +37 -62
- package/lib/model.js +3 -4
- package/lib/monk-monkey-patches.js +73 -0
- package/lib/util.js +20 -18
- package/package.json +1 -1
- package/test/blacklisting.js +83 -26
- package/test/crud.js +59 -1
- package/test/monk.js +1 -1
- package/test/util.js +7 -0
package/lib/model-validate.js
CHANGED
|
@@ -3,88 +3,63 @@ let rules = require('./rules')
|
|
|
3
3
|
|
|
4
4
|
module.exports = {
|
|
5
5
|
|
|
6
|
-
validate: function(data, opts, cb) {
|
|
6
|
+
validate: async function(data, opts, cb) {
|
|
7
7
|
/**
|
|
8
8
|
* Validates a model
|
|
9
|
-
* @param {instance} model
|
|
10
9
|
* @param {object} data
|
|
11
10
|
* @param {object} <opts>
|
|
12
|
-
* @param {
|
|
13
|
-
* @param {array|string
|
|
14
|
-
* @param {array|string}
|
|
15
|
-
* @param {
|
|
16
|
-
* default, but false on update
|
|
17
|
-
* @param {array|string|true} skipValidation - skip validation on these fields
|
|
18
|
-
* @param {boolean} timestamps - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
|
|
11
|
+
* @param {array|string|false} <opts.blacklist> - augment insertBL/updateBL, `false` will remove blacklisting
|
|
12
|
+
* @param {array|string} <opts.project> - return only these fields, ignores blacklisting
|
|
13
|
+
* @param {array|string|true} <opts.skipValidation> - skip validation on these fields
|
|
14
|
+
* @param {boolean} <opts.timestamps> - whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is
|
|
19
15
|
* updated, depending on the `options.update` value
|
|
16
|
+
* @param {boolean(false)} <opts.update> - are we validating for insert or update? todo: change to `type`
|
|
17
|
+
* @param {array|string|false} <opts.validateUndefined> - validates all 'required' undefined fields, true by
|
|
18
|
+
* default, but false on update
|
|
20
19
|
* @param {function} <cb> - instead of returning a promise
|
|
21
|
-
* @this model
|
|
22
|
-
|
|
23
20
|
* @return promise(errors[] || pruned data{})
|
|
21
|
+
* @this model
|
|
24
22
|
*/
|
|
25
23
|
|
|
26
24
|
// Optional cb and opts
|
|
27
|
-
if (util.isFunction(opts)) {
|
|
25
|
+
if (util.isFunction(opts)) {
|
|
26
|
+
cb = opts; opts = undefined
|
|
27
|
+
}
|
|
28
28
|
if (cb && !util.isFunction(cb)) {
|
|
29
29
|
throw new Error(`The callback passed to ${this.name}.validate() is not a function`)
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
try {
|
|
32
|
+
data = util.deepCopy(data)
|
|
33
|
+
opts = opts || {}
|
|
34
|
+
opts.update = opts.update || opts.findOneAndUpdate
|
|
35
|
+
opts.insert = !opts.update
|
|
36
|
+
opts.skipValidation = opts.skipValidation === true ? true : util.toArray(opts.skipValidation||[])
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
// Auguemnt the schema blacklist
|
|
45
|
-
for (let _path of opts.blacklist) {
|
|
46
|
-
let path = _path.replace(/^-/, '')
|
|
47
|
-
if (_path.match(/^-/)) whitelist.push(path)
|
|
48
|
-
else blacklist.push(path)
|
|
49
|
-
}
|
|
50
|
-
// Remove whitelisted/negated fields
|
|
51
|
-
blacklist = blacklist.filter(o => !whitelist.includes(o))
|
|
52
|
-
// Remove any deep blacklisted fields that have a whitelisted parent specified.
|
|
53
|
-
// E.g remove ['deep.deep2.deep3'] if ['deep'] exists in the whitelist
|
|
54
|
-
for (let i=blacklist.length; i--;) {
|
|
55
|
-
let split = blacklist[i].split('.')
|
|
56
|
-
for (let j=split.length; j--;) {
|
|
57
|
-
if (split.length > 1) split.pop()
|
|
58
|
-
else continue
|
|
59
|
-
if (whitelist.includes(split.join())) {
|
|
60
|
-
blacklist.splice(i, 1)
|
|
61
|
-
break
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
opts.blacklist = blacklist
|
|
66
|
-
} else {
|
|
67
|
-
opts.blacklist = [ ...this[`${opts.action}BL`] ]
|
|
68
|
-
}
|
|
38
|
+
// Get projection
|
|
39
|
+
if (opts.project) opts.projection = this._getProjectionFromProject(opts.project)
|
|
40
|
+
else opts.projection = this._getProjectionFromBlacklist(opts.update ? 'update' : 'insert', opts.blacklist)
|
|
41
|
+
|
|
42
|
+
// Hook: beforeValidate
|
|
43
|
+
await util.runSeries(this.beforeValidate.map(f => f.bind(opts, data)))
|
|
69
44
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return util.toArray(data).map(item => {
|
|
45
|
+
// Recurse and validate fields
|
|
46
|
+
let response = util.toArray(data).map(item => {
|
|
73
47
|
let validated = this._validateFields(item, this.fields, item, opts, '')
|
|
74
48
|
if (validated[0].length) throw validated[0]
|
|
75
49
|
else return validated[1]
|
|
76
50
|
})
|
|
77
51
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
52
|
+
// Single document?
|
|
53
|
+
response = util.isArray(data)? response : response[0]
|
|
54
|
+
|
|
55
|
+
// Success/error
|
|
81
56
|
if (cb) cb(null, response)
|
|
82
57
|
else return Promise.resolve(response)
|
|
83
58
|
|
|
84
|
-
}
|
|
85
|
-
if (cb) cb(
|
|
86
|
-
else throw
|
|
87
|
-
}
|
|
59
|
+
} catch (e) {
|
|
60
|
+
if (cb) cb(e)
|
|
61
|
+
else throw e
|
|
62
|
+
}
|
|
88
63
|
},
|
|
89
64
|
|
|
90
65
|
_getMostSpecificKeyMatchingPath: function(object, path) {
|
|
@@ -140,7 +115,7 @@ module.exports = {
|
|
|
140
115
|
let value = util.isArray(fields)? data : (data||{})[fieldName]
|
|
141
116
|
let indexOrFieldName = util.isArray(fields)? i : fieldName
|
|
142
117
|
let path2 = `${path}.${indexOrFieldName}`.replace(/^\./, '')
|
|
143
|
-
let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name
|
|
118
|
+
let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name = pets.name
|
|
144
119
|
let isType = 'is' + util.ucFirst(schema.type)
|
|
145
120
|
let isTypeRule = this.rules[isType] || rules[isType]
|
|
146
121
|
|
|
@@ -157,7 +132,7 @@ module.exports = {
|
|
|
157
132
|
}
|
|
158
133
|
|
|
159
134
|
// Ignore blacklisted
|
|
160
|
-
if (
|
|
135
|
+
if (this._pathBlacklisted(path3, opts.projection) && !schema.defaultOverride) return
|
|
161
136
|
// Ignore insert only
|
|
162
137
|
if (opts.update && schema.insertOnly) return
|
|
163
138
|
// Ignore virtual fields
|
|
@@ -218,8 +193,8 @@ module.exports = {
|
|
|
218
193
|
* @param {object} field - field schema
|
|
219
194
|
* @param {string} path - full field path
|
|
220
195
|
* @param {object} opts - original validate() options
|
|
221
|
-
* @this model
|
|
222
196
|
* @return {array} errors
|
|
197
|
+
* @this model
|
|
223
198
|
*/
|
|
224
199
|
let errors = []
|
|
225
200
|
if (opts.skipValidation === true) return []
|
package/lib/model.js
CHANGED
|
@@ -9,8 +9,8 @@ let Model = module.exports = function(name, opts, manager) {
|
|
|
9
9
|
* @param {string} name
|
|
10
10
|
* @param {object} opts - see mongodb colleciton documentation
|
|
11
11
|
* @param {boolean} opts.waitForIndexes
|
|
12
|
-
* @this model
|
|
13
12
|
* @return Promise(model) | this
|
|
13
|
+
* @this model
|
|
14
14
|
*/
|
|
15
15
|
if (!(this instanceof Model)) {
|
|
16
16
|
return new Model(name, opts, this)
|
|
@@ -72,7 +72,7 @@ let Model = module.exports = function(name, opts, manager) {
|
|
|
72
72
|
this._setupFields(this.fields = Object.assign({}, this._timestampFields, this.fields))
|
|
73
73
|
this.fieldsFlattened = this._getFieldsFlattened(this.fields, '') // test output?
|
|
74
74
|
|
|
75
|
-
// Extend model with monk collection
|
|
75
|
+
// Extend model with monk collection queries
|
|
76
76
|
this._collection = manager.get? manager.get(name, { castIds: false }) : null
|
|
77
77
|
if (!this._collection) {
|
|
78
78
|
this.info('There is no mongodb connection, a lot of the monk/monastery methods will be unavailable')
|
|
@@ -83,8 +83,7 @@ let Model = module.exports = function(name, opts, manager) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Add model to manager
|
|
86
|
-
if (typeof this.manager[name] === 'undefined'
|
|
87
|
-
|| typeof this.manager.model[name] !== 'undefined') {
|
|
86
|
+
if (typeof this.manager[name] === 'undefined' || typeof this.manager.model[name] !== 'undefined') {
|
|
88
87
|
this.manager[name] = this
|
|
89
88
|
} else {
|
|
90
89
|
this.warn(`Your model name '${name}' is conflicting, you are only able to
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
let MongoClient = require('mongodb').MongoClient
|
|
2
|
+
|
|
3
|
+
module.exports.open = function(uri, opts, fn) {
|
|
4
|
+
/*
|
|
5
|
+
* Monkey patch to remove db event listener warnings
|
|
6
|
+
* @todo remove when monk is removed
|
|
7
|
+
* @see https://www.mongodb.com/community/forums/t/node-44612-deprecationwarning-listening-to-events-on-
|
|
8
|
+
the-db-class-has-been-deprecated-and-will-be-removed-in-the-next-major-version/15849/4
|
|
9
|
+
*/
|
|
10
|
+
var STATE = {
|
|
11
|
+
CLOSED: 'closed',
|
|
12
|
+
OPENING: 'opening',
|
|
13
|
+
OPEN: 'open'
|
|
14
|
+
}
|
|
15
|
+
MongoClient.connect(uri, opts, function (err, client) {
|
|
16
|
+
// this = Manager
|
|
17
|
+
if (err) {
|
|
18
|
+
this._state = STATE.CLOSED
|
|
19
|
+
this.emit('error-opening', err)
|
|
20
|
+
} else {
|
|
21
|
+
this._state = STATE.OPEN
|
|
22
|
+
|
|
23
|
+
this._client = client
|
|
24
|
+
this._db = client.db()
|
|
25
|
+
|
|
26
|
+
// set up events
|
|
27
|
+
var self = this
|
|
28
|
+
;['authenticated', 'close', 'error', 'parseError', 'timeout'].forEach(function (eventName) {
|
|
29
|
+
self._client.on(eventName, function (e) {
|
|
30
|
+
self.emit(eventName, e)
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
this.emit('open', this._db)
|
|
35
|
+
}
|
|
36
|
+
if (fn) {
|
|
37
|
+
fn(err, this)
|
|
38
|
+
}
|
|
39
|
+
}.bind(this))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports.findOneAndUpdate = function(query, update, opts, fn) {
|
|
43
|
+
/*
|
|
44
|
+
* Monkey patch to use returnDocument
|
|
45
|
+
* @todo remove when monk is removed
|
|
46
|
+
* @see https://github.com/Automattic/monk/blob/master/lib/collection.js#L265
|
|
47
|
+
*/
|
|
48
|
+
// this = model
|
|
49
|
+
if (typeof opts === 'function') {
|
|
50
|
+
fn = opts
|
|
51
|
+
opts = {}
|
|
52
|
+
}
|
|
53
|
+
return this._dispatch(function findOneAndUpdate(args) {
|
|
54
|
+
var method = 'findOneAndUpdate'
|
|
55
|
+
if (typeof (args.options || {}).returnDocument === 'undefined') {
|
|
56
|
+
args.options.returnDocument = 'after'
|
|
57
|
+
}
|
|
58
|
+
if (args.options.replaceOne | args.options.replace) {
|
|
59
|
+
method = 'findOneAndReplace'
|
|
60
|
+
}
|
|
61
|
+
return args.col[method](args.query, args.update, args.options)
|
|
62
|
+
.then(function (doc) {
|
|
63
|
+
if (doc && typeof doc.value !== 'undefined') {
|
|
64
|
+
return doc.value
|
|
65
|
+
}
|
|
66
|
+
if (doc.ok && doc.lastErrorObject && doc.lastErrorObject.n === 0) {
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
return doc
|
|
70
|
+
})
|
|
71
|
+
})({options: opts, query: query, update: update, callback: fn}, 'findOneAndUpdate')
|
|
72
|
+
}
|
|
73
|
+
|
package/lib/util.js
CHANGED
|
@@ -140,14 +140,13 @@ module.exports = {
|
|
|
140
140
|
return typeof value === 'undefined'
|
|
141
141
|
},
|
|
142
142
|
|
|
143
|
-
omit: function(obj,
|
|
144
|
-
|
|
145
|
-
for (let i
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
target[i] = obj[i]
|
|
143
|
+
omit: function(obj, fields) {
|
|
144
|
+
const shallowCopy = Object.assign({}, obj)
|
|
145
|
+
for (let i=0; i<fields.length; i+=1) {
|
|
146
|
+
const key = fields[i]
|
|
147
|
+
delete shallowCopy[key]
|
|
149
148
|
}
|
|
150
|
-
return
|
|
149
|
+
return shallowCopy
|
|
151
150
|
},
|
|
152
151
|
|
|
153
152
|
parseData: function(obj) {
|
|
@@ -228,21 +227,24 @@ module.exports = {
|
|
|
228
227
|
}
|
|
229
228
|
},
|
|
230
229
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
230
|
+
pick: function(obj, keys) {
|
|
231
|
+
// Similiar to underscore.pick
|
|
232
|
+
// @param {string[] | regex[]} keys
|
|
233
|
+
if (!this.isObject(obj) && !this.isFunction(obj)) return {}
|
|
234
|
+
keys = this.toArray(keys)
|
|
235
|
+
let res = {}
|
|
236
|
+
for (let key of keys) {
|
|
237
|
+
if (this.isString(key) && obj.hasOwnProperty(key)) res[key] = obj[key]
|
|
238
|
+
if (this.isRegex(key)) {
|
|
239
|
+
for (let key2 in obj) {
|
|
240
|
+
if (obj.hasOwnProperty(key2) && key2.match(key)) res[key2] = obj[key2]
|
|
241
|
+
}
|
|
239
242
|
}
|
|
240
|
-
if (match) target[prop] = obj[prop]
|
|
241
243
|
}
|
|
242
|
-
return
|
|
244
|
+
return res
|
|
243
245
|
},
|
|
244
246
|
|
|
245
|
-
removeUndefined: (variable)
|
|
247
|
+
removeUndefined: function(variable) {
|
|
246
248
|
// takes an array or object
|
|
247
249
|
if (Array.isArray(variable)) {
|
|
248
250
|
for (let i=variable.length; i--;) {
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "monastery",
|
|
3
3
|
"description": "⛪ A straight forward MongoDB ODM built around Monk",
|
|
4
4
|
"author": "Ricky Boyce",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.36.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/test/blacklisting.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.exports = function(monastery, opendb) {
|
|
2
2
|
|
|
3
|
-
test('find blacklisting', async () => {
|
|
3
|
+
test('find blacklisting basic', async () => {
|
|
4
4
|
// Setup
|
|
5
5
|
let db = (await opendb(null)).db
|
|
6
6
|
let bird = db.model('bird', {
|
|
@@ -93,7 +93,7 @@ module.exports = function(monastery, opendb) {
|
|
|
93
93
|
}
|
|
94
94
|
}})
|
|
95
95
|
|
|
96
|
-
//
|
|
96
|
+
// initial blacklist
|
|
97
97
|
let find1 = await user.findOne({
|
|
98
98
|
query: user1._id
|
|
99
99
|
})
|
|
@@ -107,18 +107,30 @@ module.exports = function(monastery, opendb) {
|
|
|
107
107
|
deepModel: { myBird: bird1._id }
|
|
108
108
|
})
|
|
109
109
|
|
|
110
|
-
//
|
|
110
|
+
// augmented blacklist
|
|
111
111
|
let find2 = await user.findOne({
|
|
112
112
|
query: user1._id,
|
|
113
113
|
blacklist: ['pet', 'pet', 'deep', 'deepModel', '-dog', '-animals.cat']
|
|
114
114
|
})
|
|
115
|
-
|
|
115
|
+
let customBlacklist
|
|
116
|
+
expect(find2).toEqual((customBlacklist = {
|
|
116
117
|
_id: user1._id,
|
|
117
118
|
dog: 'Bruce',
|
|
118
119
|
list: [44, 54],
|
|
119
120
|
pets: [{ name: 'Pluto' }, { name: 'Milo' }],
|
|
120
121
|
animals: { dog: 'Max', cat: 'Ginger' }
|
|
122
|
+
}))
|
|
123
|
+
|
|
124
|
+
// blacklist string
|
|
125
|
+
let find3 = await user.findOne({
|
|
126
|
+
query: user1._id,
|
|
127
|
+
blacklist: 'pet pet deep deepModel -dog -animals.cat'
|
|
121
128
|
})
|
|
129
|
+
expect(find3).toEqual(customBlacklist)
|
|
130
|
+
|
|
131
|
+
// blacklist removal
|
|
132
|
+
let find4 = await user.findOne({ query: user1._id, blacklist: false })
|
|
133
|
+
expect(find4).toEqual(user1)
|
|
122
134
|
|
|
123
135
|
db.close()
|
|
124
136
|
})
|
|
@@ -219,6 +231,11 @@ module.exports = function(monastery, opendb) {
|
|
|
219
231
|
_id: user1._id,
|
|
220
232
|
bird5: { ...bird1Base, name: 'ponyo', height: 40 },
|
|
221
233
|
})
|
|
234
|
+
// blacklist removal
|
|
235
|
+
expect(await user.findOne({ query: user1._id, blacklist: false, populate: ['bird1'] })).toEqual({
|
|
236
|
+
...user1,
|
|
237
|
+
bird1: { ...bird1Base, height: 40, name: 'ponyo', wing: { size: 1, sizes: { one: 1, two: 1 }} },
|
|
238
|
+
})
|
|
222
239
|
|
|
223
240
|
db.close()
|
|
224
241
|
})
|
|
@@ -243,13 +260,13 @@ module.exports = function(monastery, opendb) {
|
|
|
243
260
|
},
|
|
244
261
|
})
|
|
245
262
|
// default
|
|
246
|
-
expect(db.user.
|
|
263
|
+
expect(db.user._getProjectionFromBlacklist('find')).toEqual({
|
|
247
264
|
'bird1.wing': 0,
|
|
248
265
|
'bird1.age': 0,
|
|
249
266
|
'password': 0,
|
|
250
267
|
})
|
|
251
268
|
// blacklist /w invalid field (which goes through)
|
|
252
|
-
expect(db.user.
|
|
269
|
+
expect(db.user._getProjectionFromBlacklist('find', ['name', 'invalidfield'])).toEqual({
|
|
253
270
|
'bird1.wing': 0,
|
|
254
271
|
'bird1.age': 0,
|
|
255
272
|
'invalidfield': 0,
|
|
@@ -257,38 +274,38 @@ module.exports = function(monastery, opendb) {
|
|
|
257
274
|
'password': 0,
|
|
258
275
|
})
|
|
259
276
|
// whitelist
|
|
260
|
-
expect(db.user.
|
|
277
|
+
expect(db.user._getProjectionFromBlacklist('find', ['-password', '-bird1.age'])).toEqual({
|
|
261
278
|
'bird1.wing': 0,
|
|
262
279
|
})
|
|
263
280
|
// whitelist parent
|
|
264
|
-
expect(db.user.
|
|
281
|
+
expect(db.user._getProjectionFromBlacklist('find', ['-bird1'])).toEqual({
|
|
265
282
|
'password': 0,
|
|
266
283
|
})
|
|
267
284
|
// whitelist parent, then blacklist child
|
|
268
|
-
expect(db.user.
|
|
285
|
+
expect(db.user._getProjectionFromBlacklist('find', ['-bird1', 'bird1.name'])).toEqual({
|
|
269
286
|
'password': 0,
|
|
270
287
|
'bird1.name': 0,
|
|
271
288
|
})
|
|
272
289
|
// the model's blacklists are applied after deep model's
|
|
273
290
|
db.user.findBL = ['-bird1.age']
|
|
274
|
-
expect(db.user.
|
|
291
|
+
expect(db.user._getProjectionFromBlacklist('find')).toEqual({
|
|
275
292
|
'bird1.wing': 0,
|
|
276
293
|
})
|
|
277
294
|
// custom blacklists are applied after the model's, which are after deep model's
|
|
278
295
|
db.user.findBL = ['-bird1.age']
|
|
279
|
-
expect(db.user.
|
|
296
|
+
expect(db.user._getProjectionFromBlacklist('find', ['bird1'])).toEqual({
|
|
280
297
|
'bird1': 0,
|
|
281
298
|
})
|
|
282
299
|
// blacklisted parent with a blacklisted child
|
|
283
|
-
expect(db.user.
|
|
300
|
+
expect(db.user._getProjectionFromBlacklist('find', ['bird1', 'bird1.wing'])).toEqual({
|
|
284
301
|
'bird1': 0,
|
|
285
302
|
})
|
|
286
303
|
// A mess of things
|
|
287
|
-
expect(db.user.
|
|
304
|
+
expect(db.user._getProjectionFromBlacklist('find', ['-bird1', 'bird1.wing', '-bird1.wing','bird1.wing.size'])).toEqual({
|
|
288
305
|
'bird1.wing.size': 0,
|
|
289
306
|
})
|
|
290
307
|
// blacklisted parent with a whitelisted child (expect blacklist expansion in future version?)
|
|
291
|
-
// expect(db.user.
|
|
308
|
+
// expect(db.user._getProjectionFromBlacklist('find', ['bird1', '-bird1.wing'])).toEqual({
|
|
292
309
|
// 'bird1.age': 0,
|
|
293
310
|
// 'bird1.name': 0,
|
|
294
311
|
// })
|
|
@@ -296,7 +313,7 @@ module.exports = function(monastery, opendb) {
|
|
|
296
313
|
db.close()
|
|
297
314
|
})
|
|
298
315
|
|
|
299
|
-
test('find project', async () => {
|
|
316
|
+
test('find project basic', async () => {
|
|
300
317
|
// Test mongodb native project option
|
|
301
318
|
// Setup
|
|
302
319
|
let db = (await opendb(null)).db
|
|
@@ -370,14 +387,14 @@ module.exports = function(monastery, opendb) {
|
|
|
370
387
|
color: { type: 'string', default: 'red' },
|
|
371
388
|
}
|
|
372
389
|
},
|
|
373
|
-
findBL: ['age']
|
|
390
|
+
findBL: ['age'],
|
|
374
391
|
})
|
|
375
392
|
let user = db.model('user', {
|
|
376
393
|
fields: {
|
|
377
394
|
dog: { type: 'string' },
|
|
378
395
|
bird: { model: 'bird' },
|
|
379
396
|
bird2: { model: 'bird' },
|
|
380
|
-
bird3: { model: 'bird' }
|
|
397
|
+
bird3: { model: 'bird' },
|
|
381
398
|
},
|
|
382
399
|
findBL: [
|
|
383
400
|
// allll these should be ignored.....?/////
|
|
@@ -390,38 +407,47 @@ module.exports = function(monastery, opendb) {
|
|
|
390
407
|
name: 'ponyo',
|
|
391
408
|
age: 3,
|
|
392
409
|
height: 40,
|
|
393
|
-
sub: {}
|
|
410
|
+
sub: {},
|
|
394
411
|
}})
|
|
395
412
|
let user1 = await user.insert({ data: {
|
|
396
413
|
dog: 'Bruce',
|
|
397
414
|
bird: bird1._id,
|
|
398
415
|
bird2: bird1._id,
|
|
399
|
-
bird3: bird1._id
|
|
416
|
+
bird3: bird1._id,
|
|
400
417
|
}})
|
|
401
418
|
|
|
402
|
-
//
|
|
419
|
+
// project
|
|
403
420
|
let find1 = await user.findOne({
|
|
404
421
|
query: user1._id,
|
|
405
422
|
populate: ['bird', 'bird2'],
|
|
406
|
-
project: ['bird.age', 'bird2']
|
|
423
|
+
project: ['bird.age', 'bird2'],
|
|
407
424
|
})
|
|
408
425
|
expect(find1).toEqual({
|
|
409
426
|
_id: user1._id,
|
|
410
427
|
bird: { age: 3 },
|
|
411
|
-
bird2: { _id: bird1._id, age: 3, name: 'ponyo', height: 40, color: 'red', sub: { color: 'red' }}
|
|
428
|
+
bird2: { _id: bird1._id, age: 3, name: 'ponyo', height: 40, color: 'red', sub: { color: 'red' }},
|
|
412
429
|
})
|
|
413
430
|
|
|
414
|
-
//
|
|
431
|
+
// project (different project details)
|
|
415
432
|
let find2 = await user.findOne({
|
|
416
433
|
query: user1._id,
|
|
417
434
|
populate: ['bird', 'bird2'],
|
|
418
|
-
project: ['bird', 'bird2.height']
|
|
435
|
+
project: ['bird', 'bird2.height'],
|
|
419
436
|
})
|
|
420
|
-
|
|
437
|
+
let customProject
|
|
438
|
+
expect(find2).toEqual((customProject={
|
|
421
439
|
_id: user1._id,
|
|
422
440
|
bird: { _id: bird1._id, age: 3, name: 'ponyo', height: 40, color: 'red', sub: { color: 'red' }},
|
|
423
441
|
bird2: { height: 40 },
|
|
442
|
+
}))
|
|
443
|
+
|
|
444
|
+
// project string
|
|
445
|
+
let find3 = await user.findOne({
|
|
446
|
+
query: user1._id,
|
|
447
|
+
populate: ['bird', 'bird2'],
|
|
448
|
+
project: 'bird bird2.height',
|
|
424
449
|
})
|
|
450
|
+
expect(find3).toEqual(customProject)
|
|
425
451
|
|
|
426
452
|
db.close()
|
|
427
453
|
})
|
|
@@ -506,7 +532,8 @@ module.exports = function(monastery, opendb) {
|
|
|
506
532
|
'-deep' // blacklist a parent
|
|
507
533
|
],
|
|
508
534
|
})
|
|
509
|
-
|
|
535
|
+
let customBlacklist
|
|
536
|
+
expect(user2).toEqual((customBlacklist = {
|
|
510
537
|
list: [44, 54],
|
|
511
538
|
dog: 'Bruce',
|
|
512
539
|
pet: 'Freddy',
|
|
@@ -522,6 +549,36 @@ module.exports = function(monastery, opendb) {
|
|
|
522
549
|
}
|
|
523
550
|
}
|
|
524
551
|
}
|
|
552
|
+
}))
|
|
553
|
+
|
|
554
|
+
// Blacklist string
|
|
555
|
+
let user3 = await user.validate(doc1, {
|
|
556
|
+
blacklist: '-dog -animals.dog pets.name -hiddenList -deep'
|
|
557
|
+
})
|
|
558
|
+
expect(user3).toEqual(customBlacklist)
|
|
559
|
+
|
|
560
|
+
// Blacklist removal
|
|
561
|
+
let user4 = await user.validate(doc1, { blacklist: false })
|
|
562
|
+
expect(user4).toEqual(doc1)
|
|
563
|
+
|
|
564
|
+
// Project whitelist
|
|
565
|
+
let user5 = await user.validate(doc1, {
|
|
566
|
+
project: [
|
|
567
|
+
'dog',
|
|
568
|
+
'pets.name',
|
|
569
|
+
'deep'
|
|
570
|
+
],
|
|
571
|
+
})
|
|
572
|
+
expect(user5).toEqual({
|
|
573
|
+
dog: 'Bruce',
|
|
574
|
+
pets: [ {name: 'Pluto'}, {name: 'Milo'} ],
|
|
575
|
+
deep: {
|
|
576
|
+
deep2: {
|
|
577
|
+
deep3: {
|
|
578
|
+
deep4: 'hideme'
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
525
582
|
})
|
|
526
583
|
db.close()
|
|
527
584
|
})
|
package/test/crud.js
CHANGED
|
@@ -423,14 +423,72 @@ module.exports = function(monastery, opendb) {
|
|
|
423
423
|
db.close()
|
|
424
424
|
})
|
|
425
425
|
|
|
426
|
-
test('
|
|
426
|
+
test('findOneAndUpdate basics', async () => {
|
|
427
|
+
// todo: test all findOneAndUpdate options
|
|
428
|
+
|
|
427
429
|
let db = (await opendb(null)).db
|
|
430
|
+
let dog = db.model('dog', {
|
|
431
|
+
fields: {
|
|
432
|
+
name: { type: 'string', default: 'Scruff' },
|
|
433
|
+
}
|
|
434
|
+
})
|
|
428
435
|
let user = db.model('user', {
|
|
436
|
+
fields: {
|
|
437
|
+
name: { type: 'string', default: 'Martin' },
|
|
438
|
+
dog: { model: 'dog' },
|
|
439
|
+
}
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
// Returns omitted field after update, i.e. dog
|
|
443
|
+
let dog1 = await dog.insert({ data: {} })
|
|
444
|
+
let user1 = await user.insert({ data: { dog: dog1._id }})
|
|
445
|
+
expect(await user.findOneAndUpdate({ query: { _id: user1._id }, data: { name: 'Martin2' }})).toEqual({
|
|
446
|
+
_id: user1._id,
|
|
447
|
+
name: 'Martin2',
|
|
448
|
+
dog: dog1._id,
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
// Returns omitted field requiring population after update, i.e. dog
|
|
452
|
+
expect(await user.findOneAndUpdate({
|
|
453
|
+
query: { _id: user1._id },
|
|
454
|
+
data: { name: 'Martin2' },
|
|
455
|
+
populate: ['dog'],
|
|
456
|
+
})).toEqual({
|
|
457
|
+
_id: user1._id,
|
|
458
|
+
name: 'Martin2',
|
|
459
|
+
dog: {
|
|
460
|
+
_id: dog1._id,
|
|
461
|
+
name: 'Scruff'
|
|
462
|
+
},
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
// Error finding document to update
|
|
466
|
+
expect(await user.findOneAndUpdate({
|
|
467
|
+
query: { _id: db.id() },
|
|
468
|
+
data: { name: 'Martin2' },
|
|
469
|
+
})).toEqual(null)
|
|
470
|
+
|
|
471
|
+
// Error finding document to update (populate)
|
|
472
|
+
expect(await user.findOneAndUpdate({
|
|
473
|
+
query: { _id: db.id() },
|
|
474
|
+
data: { name: 'Martin2' },
|
|
475
|
+
populate: ['dog'],
|
|
476
|
+
})).toEqual(null)
|
|
477
|
+
|
|
478
|
+
db.close()
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
test('remove basics', async () => {
|
|
482
|
+
let db = (await opendb(null)).db
|
|
483
|
+
let user = db.model('userRemove', {
|
|
429
484
|
fields: {
|
|
430
485
|
name: { type: 'string' },
|
|
431
486
|
},
|
|
432
487
|
})
|
|
433
488
|
|
|
489
|
+
// Clear (incase of failed a test)
|
|
490
|
+
user._remove({}, { multi: true })
|
|
491
|
+
|
|
434
492
|
// Insert multiple
|
|
435
493
|
let inserted2 = await user.insert({ data: [{ name: 'Martin' }, { name: 'Martin' }, { name: 'Martin' }]})
|
|
436
494
|
expect(inserted2).toEqual([
|
package/test/monk.js
CHANGED
package/test/util.js
CHANGED
|
@@ -39,4 +39,11 @@ module.exports = function(monastery, opendb) {
|
|
|
39
39
|
expect(db.isId('5ff50fe955da2c00170de734')).toEqual(true)
|
|
40
40
|
})
|
|
41
41
|
|
|
42
|
+
test('utilities arrayWithSchema', async () => {
|
|
43
|
+
let db = (await opendb(false)).db
|
|
44
|
+
let res = db.arrayWithSchema([{ name: { type: 'string' }}], { minLength: 1 })
|
|
45
|
+
expect(res).toContainEqual({ name: { type: 'string' }})
|
|
46
|
+
expect(res.schema).toEqual({ minLength: 1 })
|
|
47
|
+
})
|
|
48
|
+
|
|
42
49
|
}
|