monastery 1.32.5 → 1.35.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 +21 -0
- package/docs/readme.md +17 -0
- package/docs/schema/index.md +3 -3
- package/lib/index.js +3 -0
- package/lib/model-crud.js +194 -252
- package/lib/model-validate.js +16 -47
- package/lib/model.js +8 -53
- package/package.json +1 -1
- package/plugins/images/index.js +2 -2
- package/test/blacklisting.js +256 -78
- package/test/crud.js +5 -1
- package/test/model.js +0 -165
- package/test/util.js +7 -0
package/lib/model-validate.js
CHANGED
|
@@ -6,21 +6,19 @@ module.exports = {
|
|
|
6
6
|
validate: 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
|
|
@@ -31,41 +29,12 @@ module.exports = {
|
|
|
31
29
|
data = util.deepCopy(data)
|
|
32
30
|
opts = opts || {}
|
|
33
31
|
opts.insert = !opts.update
|
|
34
|
-
opts.action = opts.update? 'update' : 'insert'
|
|
35
|
-
opts.skipValidation = opts.skipValidation === true? true : util.toArray(opts.skipValidation||[])
|
|
32
|
+
opts.action = opts.update ? 'update' : 'insert'
|
|
33
|
+
opts.skipValidation = opts.skipValidation === true ? true : util.toArray(opts.skipValidation||[])
|
|
36
34
|
|
|
37
|
-
//
|
|
38
|
-
if (opts.
|
|
39
|
-
|
|
40
|
-
let blacklist = [ ...this[`${opts.action}BL`] ]
|
|
41
|
-
if (typeof opts.blacklist === 'string') {
|
|
42
|
-
opts.blacklist = opts.blacklist.trim().split(/\s+/)
|
|
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
|
-
}
|
|
35
|
+
// Get projection
|
|
36
|
+
if (opts.project) opts.projection = this._getProjectionFromProject(opts.project)
|
|
37
|
+
else opts.projection = this._getProjectionFromBlacklist(opts.action, opts.blacklist)
|
|
69
38
|
|
|
70
39
|
// Run before hook, then recurse through the model's fields
|
|
71
40
|
return util.runSeries(this.beforeValidate.map(f => f.bind(opts, data))).then(() => {
|
|
@@ -140,7 +109,7 @@ module.exports = {
|
|
|
140
109
|
let value = util.isArray(fields)? data : (data||{})[fieldName]
|
|
141
110
|
let indexOrFieldName = util.isArray(fields)? i : fieldName
|
|
142
111
|
let path2 = `${path}.${indexOrFieldName}`.replace(/^\./, '')
|
|
143
|
-
let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name
|
|
112
|
+
let path3 = path2.replace(/(^|\.)[0-9]+(\.|$)/, '$2') // no numerical keys, e.g. pets.1.name = pets.name
|
|
144
113
|
let isType = 'is' + util.ucFirst(schema.type)
|
|
145
114
|
let isTypeRule = this.rules[isType] || rules[isType]
|
|
146
115
|
|
|
@@ -157,7 +126,7 @@ module.exports = {
|
|
|
157
126
|
}
|
|
158
127
|
|
|
159
128
|
// Ignore blacklisted
|
|
160
|
-
if (
|
|
129
|
+
if (this._pathBlacklisted(path3, opts.projection) && !schema.defaultOverride) return
|
|
161
130
|
// Ignore insert only
|
|
162
131
|
if (opts.update && schema.insertOnly) return
|
|
163
132
|
// Ignore virtual fields
|
|
@@ -218,8 +187,8 @@ module.exports = {
|
|
|
218
187
|
* @param {object} field - field schema
|
|
219
188
|
* @param {string} path - full field path
|
|
220
189
|
* @param {object} opts - original validate() options
|
|
221
|
-
* @this model
|
|
222
190
|
* @return {array} errors
|
|
191
|
+
* @this model
|
|
223
192
|
*/
|
|
224
193
|
let errors = []
|
|
225
194
|
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)
|
|
@@ -69,12 +69,8 @@ let Model = module.exports = function(name, opts, manager) {
|
|
|
69
69
|
}, this)
|
|
70
70
|
|
|
71
71
|
// Extend default fields with passed in fields and check for invalid fields
|
|
72
|
-
this.fields = Object.assign({}, this._timestampFields, this.fields)
|
|
73
|
-
this.
|
|
74
|
-
|
|
75
|
-
// console.log(0, this.fieldlist)
|
|
76
|
-
// console.log(0, this.findBL)
|
|
77
|
-
// console.log(0, this.findBLProject)
|
|
72
|
+
this._setupFields(this.fields = Object.assign({}, this._timestampFields, this.fields))
|
|
73
|
+
this.fieldsFlattened = this._getFieldsFlattened(this.fields, '') // test output?
|
|
78
74
|
|
|
79
75
|
// Extend model with monk collection actions
|
|
80
76
|
this._collection = manager.get? manager.get(name, { castIds: false }) : null
|
|
@@ -107,52 +103,24 @@ let Model = module.exports = function(name, opts, manager) {
|
|
|
107
103
|
else this._setupIndexes().catch(errHandler) // returns this
|
|
108
104
|
}
|
|
109
105
|
|
|
110
|
-
Model.prototype._getFieldlist = function(fields, path) {
|
|
111
|
-
/**
|
|
112
|
-
* Get all field paths (without array indices, handy for blacklisting)
|
|
113
|
-
* @param {object|array} fields - subdocument or array
|
|
114
|
-
* @param {string} path
|
|
115
|
-
* @return {array} e.g. ['name', 'pets.dog']
|
|
116
|
-
*/
|
|
117
|
-
let list = []
|
|
118
|
-
util.forEach(fields, function(field, fieldName) {
|
|
119
|
-
// Don't append array indexes to the new path e.g. '0.'
|
|
120
|
-
let newPath = util.isArray(fields)? path : path + fieldName + '.'
|
|
121
|
-
if (fieldName == 'schema') {
|
|
122
|
-
return
|
|
123
|
-
/*} else if (this.findBL.includes(newPath.replace(/\.$/, ''))) {
|
|
124
|
-
return*/
|
|
125
|
-
} else if (util.isArray(field)) {
|
|
126
|
-
list = list.concat(this._getFieldlist(field, newPath))
|
|
127
|
-
} else if (util.isSubdocument(field)) {
|
|
128
|
-
list = list.concat(this._getFieldlist(field, newPath))
|
|
129
|
-
} else {
|
|
130
|
-
list.push(newPath.replace(/\.$/, ''))
|
|
131
|
-
}
|
|
132
|
-
}, this)
|
|
133
|
-
return list
|
|
134
|
-
}
|
|
135
|
-
|
|
136
106
|
Model.prototype._getFieldsFlattened = function(fields, path) {
|
|
137
107
|
/**
|
|
138
108
|
* Flatten fields
|
|
139
|
-
* @param {object|array} fields - subdocument or array
|
|
109
|
+
* @param {object|array} fields - can be a nested subdocument or array
|
|
140
110
|
* @param {string} path
|
|
141
|
-
* @return {object} e.g.
|
|
111
|
+
* @return {object} e.g. {'name': Schema, 'pets.dog': Schema}
|
|
142
112
|
*/
|
|
143
113
|
let obj = {}
|
|
144
114
|
util.forEach(fields, function(field, fieldName) {
|
|
145
|
-
let newPath = path + fieldName + '.'
|
|
115
|
+
let newPath = /*util.isArray(fields)? path : */path + fieldName + '.'
|
|
146
116
|
if (fieldName == 'schema') {
|
|
147
117
|
return
|
|
148
118
|
} else if (util.isArray(field)) {
|
|
149
119
|
Object.assign(obj, this._getFieldsFlattened(field, newPath))
|
|
150
120
|
} else if (util.isSubdocument(field)) {
|
|
151
121
|
Object.assign(obj, this._getFieldsFlattened(field, newPath))
|
|
152
|
-
} else
|
|
153
|
-
|
|
154
|
-
obj[newPath.replace(/\.$/, '')] = field
|
|
155
|
-
}
|
|
122
|
+
} else {
|
|
123
|
+
obj[newPath.replace(/\.$/, '')] = field
|
|
156
124
|
}
|
|
157
125
|
}, this)
|
|
158
126
|
return obj
|
|
@@ -234,19 +202,6 @@ Model.prototype._setupFields = function(fields) {
|
|
|
234
202
|
}, this)
|
|
235
203
|
},
|
|
236
204
|
|
|
237
|
-
Model.prototype._setupFieldsAndWhitelists = function(fields, path) {
|
|
238
|
-
/**
|
|
239
|
-
* Setup fields and retrieve a handy schema field list. This can be called mulitple times.
|
|
240
|
-
* Note: the project fields are only required when finding with projections.
|
|
241
|
-
* @param {object} fields - can be a nested subdocument or array
|
|
242
|
-
* @param {string} <path>
|
|
243
|
-
*/
|
|
244
|
-
this._setupFields(fields)
|
|
245
|
-
this.fieldlist = this._getFieldlist(fields, path || '')
|
|
246
|
-
this.defaultFieldsFlattened = this._getFieldsFlattened(fields, path || '') // test output?
|
|
247
|
-
this.findBLProject = this.findBL.reduce((o, v) => { (o[v] = 0); return o }, {})
|
|
248
|
-
},
|
|
249
|
-
|
|
250
205
|
Model.prototype._setupIndexes = function(fields, opts={}) {
|
|
251
206
|
/**
|
|
252
207
|
* Creates indexes for the model (multikey, and sub-document supported)
|
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.35.0",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/plugins/images/index.js
CHANGED
|
@@ -56,8 +56,8 @@ let plugin = module.exports = {
|
|
|
56
56
|
model.imageFields = plugin._findAndTransformImageFields(model.fields, '')
|
|
57
57
|
|
|
58
58
|
if (model.imageFields.length) {
|
|
59
|
-
// Todo?: Update image fields
|
|
60
|
-
// model.
|
|
59
|
+
// Todo?: Update image fields / blacklists with the new object schema
|
|
60
|
+
// model._setupFields(model.fields)/model._getFieldsFlattened(model.fields)
|
|
61
61
|
model.beforeValidate.push(function(data, n) {
|
|
62
62
|
plugin.keepImagePlacement(this, data).then(() => n(null, data)).catch(e => n(e))
|
|
63
63
|
})
|
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,9 +93,7 @@ module.exports = function(monastery, opendb) {
|
|
|
93
93
|
}
|
|
94
94
|
}})
|
|
95
95
|
|
|
96
|
-
//
|
|
97
|
-
|
|
98
|
-
// Test initial blacklist
|
|
96
|
+
// initial blacklist
|
|
99
97
|
let find1 = await user.findOne({
|
|
100
98
|
query: user1._id
|
|
101
99
|
})
|
|
@@ -109,50 +107,214 @@ module.exports = function(monastery, opendb) {
|
|
|
109
107
|
deepModel: { myBird: bird1._id }
|
|
110
108
|
})
|
|
111
109
|
|
|
112
|
-
//
|
|
110
|
+
// augmented blacklist
|
|
113
111
|
let find2 = await user.findOne({
|
|
114
112
|
query: user1._id,
|
|
115
113
|
blacklist: ['pet', 'pet', 'deep', 'deepModel', '-dog', '-animals.cat']
|
|
116
114
|
})
|
|
117
|
-
|
|
115
|
+
let customBlacklist
|
|
116
|
+
expect(find2).toEqual((customBlacklist = {
|
|
118
117
|
_id: user1._id,
|
|
119
118
|
dog: 'Bruce',
|
|
120
119
|
list: [44, 54],
|
|
121
120
|
pets: [{ name: 'Pluto' }, { name: 'Milo' }],
|
|
122
121
|
animals: { dog: 'Max', cat: 'Ginger' }
|
|
123
|
-
})
|
|
122
|
+
}))
|
|
124
123
|
|
|
125
|
-
//
|
|
124
|
+
// blacklist string
|
|
126
125
|
let find3 = await user.findOne({
|
|
127
126
|
query: user1._id,
|
|
128
|
-
|
|
129
|
-
})
|
|
130
|
-
expect(find3).toEqual({
|
|
131
|
-
_id: user1._id,
|
|
132
|
-
dog: 'Bruce',
|
|
133
|
-
list: [44, 54],
|
|
134
|
-
pets: [{ age: 5 }, { age: 4 }]
|
|
127
|
+
blacklist: 'pet pet deep deepModel -dog -animals.cat'
|
|
135
128
|
})
|
|
129
|
+
expect(find3).toEqual(customBlacklist)
|
|
136
130
|
|
|
137
|
-
//
|
|
138
|
-
let
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
131
|
+
// blacklist removal
|
|
132
|
+
let find4 = await user.findOne({ query: user1._id, blacklist: false })
|
|
133
|
+
expect(find4).toEqual(user1)
|
|
134
|
+
|
|
135
|
+
db.close()
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
test('find blacklisting population', async () => {
|
|
139
|
+
// inprogresss
|
|
140
|
+
// Setup
|
|
141
|
+
let db = monastery('localhost/monastery', {
|
|
142
|
+
timestamps: false,
|
|
143
|
+
serverSelectionTimeoutMS: 2000,
|
|
144
|
+
})
|
|
145
|
+
let bird = db.model('bird', {
|
|
146
|
+
fields: {
|
|
147
|
+
color: { type: 'string', default: 'red' },
|
|
148
|
+
height: { type: 'number' },
|
|
149
|
+
name: { type: 'string' },
|
|
150
|
+
sub: {
|
|
151
|
+
color: { type: 'string', default: 'red' },
|
|
152
|
+
},
|
|
153
|
+
subs: [{
|
|
154
|
+
color: { type: 'string', default: 'red'},
|
|
155
|
+
}],
|
|
156
|
+
wing: {
|
|
157
|
+
size: { type: 'number' },
|
|
158
|
+
sizes: {
|
|
159
|
+
one: { type: 'number' },
|
|
160
|
+
two: { type: 'number' },
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
findBL: ['wing']
|
|
165
|
+
})
|
|
166
|
+
let user = db.model('user', {
|
|
167
|
+
fields: {
|
|
168
|
+
dog: { type: 'string' },
|
|
169
|
+
bird1: { model: 'bird' },
|
|
170
|
+
bird2: { model: 'bird' },
|
|
171
|
+
bird3: { model: 'bird' },
|
|
172
|
+
bird4: { model: 'bird' },
|
|
173
|
+
bird5: { model: 'bird' },
|
|
174
|
+
},
|
|
175
|
+
findBL: [
|
|
176
|
+
'bird1.name', // bird1.name & bird1.wing blacklisted
|
|
177
|
+
'-bird2', 'bird2.name', // bird2.name blacklisted
|
|
178
|
+
'bird3.name', '-bird3', 'bird3.height', // bird3.height blacklisted
|
|
179
|
+
'-bird4.wing.sizes.one', '-bird4.wing.size', // ignored
|
|
180
|
+
// bird4.wing.sizes.two blacklisted (expand in future verion)
|
|
181
|
+
'-bird5.wing.sizes.one', // bird5.wing.sizes.one ignored, wing blacklisted
|
|
182
|
+
// bird5.wing.sizes.two, wing.size blacklisted (expand in future verion)
|
|
143
183
|
]
|
|
144
184
|
})
|
|
145
|
-
|
|
185
|
+
let bird1 = await bird.insert({
|
|
186
|
+
data: {
|
|
187
|
+
name: 'ponyo',
|
|
188
|
+
height: 40,
|
|
189
|
+
sub: {},
|
|
190
|
+
wing: { size: 1, sizes: { one: 1, two: 1 }}
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
let userData = {
|
|
194
|
+
dog: 'Bruce',
|
|
195
|
+
bird1: bird1._id,
|
|
196
|
+
bird2: bird1._id,
|
|
197
|
+
bird3: bird1._id,
|
|
198
|
+
bird4: bird1._id,
|
|
199
|
+
bird5: bird1._id
|
|
200
|
+
}
|
|
201
|
+
let user1 = await user.insert({ data: userData })
|
|
202
|
+
let bird1Base = { _id: bird1._id, color: 'red', sub: { color: 'red' }}
|
|
203
|
+
|
|
204
|
+
// Test bird1
|
|
205
|
+
expect(await user.findOne({ query: user1._id, populate: ['bird1'] })).toEqual({
|
|
206
|
+
...userData,
|
|
207
|
+
_id: user1._id,
|
|
208
|
+
bird1: { ...bird1Base, height: 40 },
|
|
209
|
+
})
|
|
210
|
+
// Test bird2
|
|
211
|
+
expect(await user.findOne({ query: user1._id, populate: ['bird2'] })).toEqual({
|
|
212
|
+
...userData,
|
|
146
213
|
_id: user1._id,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
214
|
+
bird2: { ...bird1Base, height: 40, wing: { size: 1, sizes: { one: 1, two: 1 }} },
|
|
215
|
+
})
|
|
216
|
+
// Test bird3
|
|
217
|
+
expect(await user.findOne({ query: user1._id, populate: ['bird3'] })).toEqual({
|
|
218
|
+
...userData,
|
|
219
|
+
_id: user1._id,
|
|
220
|
+
bird3: { ...bird1Base, name: 'ponyo', wing: { size: 1, sizes: { one: 1, two: 1 }} },
|
|
221
|
+
})
|
|
222
|
+
// Test bird4
|
|
223
|
+
expect(await user.findOne({ query: user1._id, populate: ['bird4'] })).toEqual({
|
|
224
|
+
...userData,
|
|
225
|
+
_id: user1._id,
|
|
226
|
+
bird4: { ...bird1Base, name: 'ponyo', height: 40 },
|
|
227
|
+
})
|
|
228
|
+
// Test bird5
|
|
229
|
+
expect(await user.findOne({ query: user1._id, populate: ['bird5'] })).toEqual({
|
|
230
|
+
...userData,
|
|
231
|
+
_id: user1._id,
|
|
232
|
+
bird5: { ...bird1Base, name: 'ponyo', height: 40 },
|
|
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
|
+
})
|
|
239
|
+
|
|
240
|
+
db.close()
|
|
241
|
+
})
|
|
242
|
+
|
|
243
|
+
test('find blacklisting getProjection', async () => {
|
|
244
|
+
let db = (await opendb(null)).db
|
|
245
|
+
// Setup
|
|
246
|
+
db.model('bird', {
|
|
247
|
+
fields: {
|
|
248
|
+
age: { type: 'number' },
|
|
249
|
+
name: { type: 'string' },
|
|
250
|
+
wing: {
|
|
251
|
+
size: { type: 'number' },
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
findBL: ['age', 'wing']
|
|
255
|
+
})
|
|
256
|
+
db.model('user', {
|
|
257
|
+
fields: {
|
|
258
|
+
name: { type: 'string' },
|
|
259
|
+
bird1: { model: 'bird' },
|
|
260
|
+
},
|
|
261
|
+
})
|
|
262
|
+
// default
|
|
263
|
+
expect(db.user._getProjectionFromBlacklist('find')).toEqual({
|
|
264
|
+
'bird1.wing': 0,
|
|
265
|
+
'bird1.age': 0,
|
|
266
|
+
'password': 0,
|
|
267
|
+
})
|
|
268
|
+
// blacklist /w invalid field (which goes through)
|
|
269
|
+
expect(db.user._getProjectionFromBlacklist('find', ['name', 'invalidfield'])).toEqual({
|
|
270
|
+
'bird1.wing': 0,
|
|
271
|
+
'bird1.age': 0,
|
|
272
|
+
'invalidfield': 0,
|
|
273
|
+
'name': 0,
|
|
274
|
+
'password': 0,
|
|
275
|
+
})
|
|
276
|
+
// whitelist
|
|
277
|
+
expect(db.user._getProjectionFromBlacklist('find', ['-password', '-bird1.age'])).toEqual({
|
|
278
|
+
'bird1.wing': 0,
|
|
279
|
+
})
|
|
280
|
+
// whitelist parent
|
|
281
|
+
expect(db.user._getProjectionFromBlacklist('find', ['-bird1'])).toEqual({
|
|
282
|
+
'password': 0,
|
|
283
|
+
})
|
|
284
|
+
// whitelist parent, then blacklist child
|
|
285
|
+
expect(db.user._getProjectionFromBlacklist('find', ['-bird1', 'bird1.name'])).toEqual({
|
|
286
|
+
'password': 0,
|
|
287
|
+
'bird1.name': 0,
|
|
150
288
|
})
|
|
289
|
+
// the model's blacklists are applied after deep model's
|
|
290
|
+
db.user.findBL = ['-bird1.age']
|
|
291
|
+
expect(db.user._getProjectionFromBlacklist('find')).toEqual({
|
|
292
|
+
'bird1.wing': 0,
|
|
293
|
+
})
|
|
294
|
+
// custom blacklists are applied after the model's, which are after deep model's
|
|
295
|
+
db.user.findBL = ['-bird1.age']
|
|
296
|
+
expect(db.user._getProjectionFromBlacklist('find', ['bird1'])).toEqual({
|
|
297
|
+
'bird1': 0,
|
|
298
|
+
})
|
|
299
|
+
// blacklisted parent with a blacklisted child
|
|
300
|
+
expect(db.user._getProjectionFromBlacklist('find', ['bird1', 'bird1.wing'])).toEqual({
|
|
301
|
+
'bird1': 0,
|
|
302
|
+
})
|
|
303
|
+
// A mess of things
|
|
304
|
+
expect(db.user._getProjectionFromBlacklist('find', ['-bird1', 'bird1.wing', '-bird1.wing','bird1.wing.size'])).toEqual({
|
|
305
|
+
'bird1.wing.size': 0,
|
|
306
|
+
})
|
|
307
|
+
// blacklisted parent with a whitelisted child (expect blacklist expansion in future version?)
|
|
308
|
+
// expect(db.user._getProjectionFromBlacklist('find', ['bird1', '-bird1.wing'])).toEqual({
|
|
309
|
+
// 'bird1.age': 0,
|
|
310
|
+
// 'bird1.name': 0,
|
|
311
|
+
// })
|
|
151
312
|
|
|
152
313
|
db.close()
|
|
153
314
|
})
|
|
154
315
|
|
|
155
|
-
test('find
|
|
316
|
+
test('find project basic', async () => {
|
|
317
|
+
// Test mongodb native project option
|
|
156
318
|
// Setup
|
|
157
319
|
let db = (await opendb(null)).db
|
|
158
320
|
let user = db.model('user', {
|
|
@@ -184,7 +346,6 @@ module.exports = function(monastery, opendb) {
|
|
|
184
346
|
let find1 = await user.findOne({
|
|
185
347
|
query: user1._id,
|
|
186
348
|
project: ['animal.name', 'animals.name']
|
|
187
|
-
//blacklist: ['animal.color', 'animals']
|
|
188
349
|
})
|
|
189
350
|
expect(find1).toEqual({
|
|
190
351
|
_id: user1._id,
|
|
@@ -209,98 +370,84 @@ module.exports = function(monastery, opendb) {
|
|
|
209
370
|
]
|
|
210
371
|
})
|
|
211
372
|
|
|
212
|
-
// Test exclusion blacklist
|
|
213
|
-
let find3 = await user.findOne({
|
|
214
|
-
query: user1._id,
|
|
215
|
-
blacklist: ['animal.color', 'animals', 'color']
|
|
216
|
-
})
|
|
217
|
-
expect(find3).toEqual({
|
|
218
|
-
_id: user1._id,
|
|
219
|
-
name: 'Bruce',
|
|
220
|
-
animal: { name: 'max' }
|
|
221
|
-
})
|
|
222
|
-
|
|
223
373
|
db.close()
|
|
224
374
|
})
|
|
225
375
|
|
|
226
|
-
test('find
|
|
376
|
+
test('find project population', async () => {
|
|
377
|
+
// Test mongodb native project option
|
|
227
378
|
// Setup
|
|
228
379
|
let db = (await opendb(null)).db
|
|
229
380
|
let bird = db.model('bird', {
|
|
230
381
|
fields: {
|
|
231
382
|
name: { type: 'string' },
|
|
232
383
|
age: { type: 'number' },
|
|
384
|
+
height: { type: 'number' },
|
|
233
385
|
color: { type: 'string', default: 'red' },
|
|
234
386
|
sub: {
|
|
235
387
|
color: { type: 'string', default: 'red' },
|
|
236
388
|
}
|
|
237
|
-
}
|
|
389
|
+
},
|
|
390
|
+
findBL: ['age'],
|
|
238
391
|
})
|
|
239
392
|
let user = db.model('user', {
|
|
240
393
|
fields: {
|
|
241
394
|
dog: { type: 'string' },
|
|
242
|
-
|
|
243
|
-
|
|
395
|
+
bird: { model: 'bird' },
|
|
396
|
+
bird2: { model: 'bird' },
|
|
397
|
+
bird3: { model: 'bird' },
|
|
244
398
|
},
|
|
399
|
+
findBL: [
|
|
400
|
+
// allll these should be ignored.....?/////
|
|
401
|
+
'bird.name', // bird.name & bird.age blacklisted
|
|
402
|
+
'-bird2', 'bird2.name', // bird2.name blacklisted
|
|
403
|
+
'bird3.name', '-bird3', 'bird3.height', // bird3.height blacklisted
|
|
404
|
+
]
|
|
245
405
|
})
|
|
246
406
|
let bird1 = await bird.insert({ data: {
|
|
247
407
|
name: 'ponyo',
|
|
248
408
|
age: 3,
|
|
249
|
-
|
|
409
|
+
height: 40,
|
|
410
|
+
sub: {},
|
|
250
411
|
}})
|
|
251
412
|
let user1 = await user.insert({ data: {
|
|
252
413
|
dog: 'Bruce',
|
|
253
|
-
|
|
254
|
-
|
|
414
|
+
bird: bird1._id,
|
|
415
|
+
bird2: bird1._id,
|
|
416
|
+
bird3: bird1._id,
|
|
255
417
|
}})
|
|
256
418
|
|
|
257
|
-
//
|
|
419
|
+
// project
|
|
258
420
|
let find1 = await user.findOne({
|
|
259
421
|
query: user1._id,
|
|
260
|
-
populate: ['
|
|
261
|
-
project: ['
|
|
422
|
+
populate: ['bird', 'bird2'],
|
|
423
|
+
project: ['bird.age', 'bird2'],
|
|
262
424
|
})
|
|
263
425
|
expect(find1).toEqual({
|
|
264
426
|
_id: user1._id,
|
|
265
|
-
|
|
266
|
-
|
|
427
|
+
bird: { age: 3 },
|
|
428
|
+
bird2: { _id: bird1._id, age: 3, name: 'ponyo', height: 40, color: 'red', sub: { color: 'red' }},
|
|
267
429
|
})
|
|
268
430
|
|
|
269
|
-
//
|
|
431
|
+
// project (different project details)
|
|
270
432
|
let find2 = await user.findOne({
|
|
271
433
|
query: user1._id,
|
|
272
|
-
populate: ['
|
|
273
|
-
project: ['
|
|
274
|
-
})
|
|
275
|
-
expect(find2).toEqual({
|
|
276
|
-
_id: user1._id,
|
|
277
|
-
myBird: { _id: bird1._id, age: 3, name: 'ponyo', color: 'red', sub: { color: 'red' }},
|
|
278
|
-
myBird2: { age: 3 },
|
|
279
|
-
})
|
|
280
|
-
|
|
281
|
-
// Test blacklisting
|
|
282
|
-
let find22 = await user.findOne({
|
|
283
|
-
query: user1._id,
|
|
284
|
-
populate: ['myBird', 'myBird2'],
|
|
285
|
-
blacklist: ['dog', 'myBird2.name', 'myBird2._id']
|
|
434
|
+
populate: ['bird', 'bird2'],
|
|
435
|
+
project: ['bird', 'bird2.height'],
|
|
286
436
|
})
|
|
287
|
-
|
|
437
|
+
let customProject
|
|
438
|
+
expect(find2).toEqual((customProject={
|
|
288
439
|
_id: user1._id,
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
})
|
|
440
|
+
bird: { _id: bird1._id, age: 3, name: 'ponyo', height: 40, color: 'red', sub: { color: 'red' }},
|
|
441
|
+
bird2: { height: 40 },
|
|
442
|
+
}))
|
|
292
443
|
|
|
293
|
-
//
|
|
444
|
+
// project string
|
|
294
445
|
let find3 = await user.findOne({
|
|
295
446
|
query: user1._id,
|
|
296
|
-
populate: ['
|
|
297
|
-
|
|
298
|
-
})
|
|
299
|
-
expect(find3).toEqual({
|
|
300
|
-
_id: user1._id,
|
|
301
|
-
myBird: { _id: bird1._id, age: 3, name: 'ponyo', color: 'red', sub: { color: 'red' }},
|
|
302
|
-
myBird2: { _id: bird1._id, age: 3, name: 'ponyo', color: 'red', sub: { color: 'red' }}
|
|
447
|
+
populate: ['bird', 'bird2'],
|
|
448
|
+
project: 'bird bird2.height',
|
|
303
449
|
})
|
|
450
|
+
expect(find3).toEqual(customProject)
|
|
304
451
|
|
|
305
452
|
db.close()
|
|
306
453
|
})
|
|
@@ -385,7 +532,8 @@ module.exports = function(monastery, opendb) {
|
|
|
385
532
|
'-deep' // blacklist a parent
|
|
386
533
|
],
|
|
387
534
|
})
|
|
388
|
-
|
|
535
|
+
let customBlacklist
|
|
536
|
+
expect(user2).toEqual((customBlacklist = {
|
|
389
537
|
list: [44, 54],
|
|
390
538
|
dog: 'Bruce',
|
|
391
539
|
pet: 'Freddy',
|
|
@@ -401,6 +549,36 @@ module.exports = function(monastery, opendb) {
|
|
|
401
549
|
}
|
|
402
550
|
}
|
|
403
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
|
+
}
|
|
404
582
|
})
|
|
405
583
|
db.close()
|
|
406
584
|
})
|