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.
@@ -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 {boolean(false)} update - are we validating for insert or update?
13
- * @param {array|string|false} blacklist - augment schema blacklist, `false` will remove all blacklisting
14
- * @param {array|string} projection - only return these fields, ignores blacklist
15
- * @param {array|string|false} validateUndefined - validates all 'required' undefined fields, true by
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
- // Blacklist
38
- if (opts.blacklist) {
39
- let whitelist = []
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 (opts.blacklist.indexOf(path3) >= 0 && !schema.defaultOverride) return
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._setupFieldsAndWhitelists(this.fields)
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. ['name', 'pets.dog']
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 if (fieldName != 'createdAt' && fieldName != 'updatedAt') {
153
- if (util.isDefined(field.default)) {
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.32.5",
5
+ "version": "1.35.0",
6
6
  "license": "MIT",
7
7
  "repository": "github:boycce/monastery",
8
8
  "homepage": "https://boycce.github.io/monastery/",
@@ -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 and whitelists with the new object schema
60
- // model._setupFieldsAndWhitelists(model.fields)
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
  })
@@ -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
- // Note: testing mongodb projections
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
- // Test augmented blacklist
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
- expect(find2).toEqual({
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
- // Test positive projection
124
+ // blacklist string
126
125
  let find3 = await user.findOne({
127
126
  query: user1._id,
128
- project: ['dog', 'list', 'pets.age']
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
- // Test negative projection
138
- let find5 = await user.findOne({
139
- query: user1._id,
140
- project: [
141
- '-list', '-hiddenDeepModel', '-pet', '-pet', '-pets', '-deep', '-deeper', '-deepModel',
142
- '-dog', '-animals.cat'
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
- expect(find5).toEqual({
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
- animals: { dog: 'Max' },
148
- hiddenList: [12, 23],
149
- hiddenPets: [{ name: 'secretPet' }]
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 blacklisting (default fields)', async () => {
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 blacklisting (populate)', async () => {
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
- myBird: { model: 'bird' },
243
- myBird2: { model: 'bird' }
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
- sub: {}
409
+ height: 40,
410
+ sub: {},
250
411
  }})
251
412
  let user1 = await user.insert({ data: {
252
413
  dog: 'Bruce',
253
- myBird: bird1._id,
254
- myBird2: bird1._id
414
+ bird: bird1._id,
415
+ bird2: bird1._id,
416
+ bird3: bird1._id,
255
417
  }})
256
418
 
257
- // Test project
419
+ // project
258
420
  let find1 = await user.findOne({
259
421
  query: user1._id,
260
- populate: ['myBird', 'myBird2'],
261
- project: ['myBird.age', 'myBird2']
422
+ populate: ['bird', 'bird2'],
423
+ project: ['bird.age', 'bird2'],
262
424
  })
263
425
  expect(find1).toEqual({
264
426
  _id: user1._id,
265
- myBird: { age: 3 },
266
- myBird2: { _id: bird1._id, age: 3, name: 'ponyo', color: 'red', sub: { color: 'red' }},
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
- // Test project (different project details)
431
+ // project (different project details)
270
432
  let find2 = await user.findOne({
271
433
  query: user1._id,
272
- populate: ['myBird', 'myBird2'],
273
- project: ['myBird', 'myBird2.age']
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
- expect(find22).toEqual({
437
+ let customProject
438
+ expect(find2).toEqual((customProject={
288
439
  _id: user1._id,
289
- myBird: { _id: bird1._id, age: 3, name: 'ponyo', color: 'red', sub: { color: 'red' }},
290
- myBird2: { age: 3, color: 'red', sub: { color: 'red' }}
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
- // Test blacklisting overrides
444
+ // project string
294
445
  let find3 = await user.findOne({
295
446
  query: user1._id,
296
- populate: ['myBird', 'myBird2'],
297
- blacklist: ['dog', 'myBird2.name', 'myBird2._id', '-myBird2']
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
- expect(user2).toEqual({
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
  })