monastery 1.36.1 → 1.36.2

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.
@@ -8,9 +8,9 @@ has_children: true
8
8
 
9
9
  Created via [`manager.model`](../manager/model).
10
10
 
11
- #### Monk collection instance methods
11
+ #### Monk collection instance operators
12
12
 
13
- Additionally models inherit most of the [monk collection](https://automattic.github.io/monk/docs/collection/) instance methods which are available under `model`.
13
+ Additionally models inherit most of the [monk collection](https://automattic.github.io/monk/docs/collection/) instance operators which are available under `model`.
14
14
 
15
15
  * model.[_aggregate](https://automattic.github.io/monk/docs/collection/aggregate.html)
16
16
  * model.[_bulkWrite](https://automattic.github.io/monk/docs/collection/bulkWrite.html)
@@ -5,16 +5,17 @@ parent: Model
5
5
 
6
6
  # `model.insert`
7
7
 
8
- Validate and insert document(s) in a collection and call related hooks: `schema.beforeInsert`, `schema.afterInsert`
8
+ Validate and insert document(s) in a collection and calls model hooks: `beforeInsert`, `afterInsert`
9
9
 
10
10
  ### Arguments
11
11
 
12
12
  `options` *(object)*
13
13
 
14
- - `options.data` *(object\|array)* - Data that is validated against the model schema. Key names can be in dot or bracket notation which is handy for HTML FormData.
15
- - [`options.skipValidation`] (string\|array): skip validation for this field name(s)
16
- - [`options.timestamps`] *(boolean)*: whether `createdAt` and `updatedAt` are automatically inserted, defaults to `manager.timestamps`
17
- - [`options.blacklist`] *(array\|string\|false)*: augment `schema.insertBL`. `false` will remove all blacklisting
14
+ - `data` *(object\|array)* - Data that is validated against the model fields. Key names can be in dot or bracket notation which is handy for HTML FormData.
15
+ - [[`blacklist`](#blacklisting)] *(array\|string\|false)*: augment `definition.insertBL`. `false` will remove all blacklisting
16
+ - [`project`] *(string\|array\|object)*: project these fields, ignores blacklisting
17
+ - [`skipValidation`] (string\|array): skip validation for this field name(s)
18
+ - [`timestamps`] *(boolean)*: whether `createdAt` and `updatedAt` are automatically inserted, defaults to `manager.timestamps`
18
19
  - [[`any mongodb option`](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#insert)] *(any)*
19
20
 
20
21
  [`callback`] *(function)*: pass instead of return a promise
@@ -32,32 +33,33 @@ user.insert({ data: [{ name: 'Martin Luther' }, { name: 'Bruce Lee' }]})
32
33
 
33
34
  ### Blacklisting
34
35
 
35
- You can augment the model's `schema.insertBL` blacklist by passing a custom `blacklist`:
36
+ You can augment the model's blacklist (`definition.insertBL`) by passing a custom `blacklist`:
36
37
 
37
38
  ```js
38
39
  // Prevents `name` and `pets.$.name` (array) from being returned.
39
40
  user.insert({ data: {}, blacklist: ['name', 'pets.name'] })
40
- // You can also whitelist any blacklisted fields found in schema.insertBL
41
+ // You can also whitelist any blacklisted fields found in definition.insertBL
41
42
  user.insert({ data: {}, blacklist: ['-name', '-pet'] })
42
43
  ```
43
44
 
44
45
  ### Defaults example
45
46
 
46
- When defaultObjects is enabled, undefined subdocuments and arrays will default to `{}` `[]` respectively when inserting. You can enable `defaultObjects` via the [manager options](../manager#arguments).
47
+ When defaultObjects is enabled, undefined embedded documents and arrays will default to `{}` `[]` respectively when inserting. You can enable `defaultObjects` via the [manager options](../manager#arguments).
47
48
 
48
49
  ```js
49
- db.model({ fields: {
50
- names: [{ type: 'string' }],
51
- pets: {
52
- name: { type: 'string' },
53
- colors: [{ type: 'string' }]
50
+ db.model({
51
+ fields: {
52
+ names: [{ type: 'string' }],
53
+ pets: {
54
+ name: { type: 'string' },
55
+ colors: [{ type: 'string' }]
56
+ }
54
57
  }
55
- }})
56
-
57
- user.insert({ data: {} }).then(data => {
58
- // data = {
59
- // names: [],
60
- // pets: { colors: [] }
61
- // }
62
58
  })
59
+
60
+ await user.insert({ data: {} })
61
+ // data = {
62
+ // names: [],
63
+ // pets: { colors: [] }
64
+ // }
63
65
  ```
@@ -5,14 +5,14 @@ parent: Model
5
5
 
6
6
  # `model.remove`
7
7
 
8
- Remove document(s) in a collection and call related hooks: `schema.beforeRemove`, `schema.afterRemove`
8
+ Remove document(s) in a collection and calls model hooks: `beforeRemove`, `afterRemove`
9
9
 
10
10
  ### Arguments
11
11
 
12
12
  `options` *(object)*
13
13
 
14
- - `options.query` *(object\|id)*
15
- - [`options.sort`] *(string\|object\|array)*: same as the mongodb option, but allows for string parsing e.g. 'name', 'name:1'
14
+ - `query` *(object\|id)*
15
+ - [`sort`] *(string\|object\|array)*: same as the mongodb option, but allows for string parsing e.g. 'name', 'name:1'
16
16
  - [[`any mongodb option`](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#remove)] *(any)*
17
17
 
18
18
  [`callback`] *(function)*: pass instead of return a promise
@@ -5,18 +5,19 @@ parent: Model
5
5
 
6
6
  # `model.update`
7
7
 
8
- Update document(s) in a collection and call related hooks: `schema.beforeUpdate`, `schema.afterUpdate`. By default this method method updates a single document. Set the `multi` mongodb option to update all documents that match the query criteria.
8
+ Update document(s) in a collection and calls model hooks: `beforeUpdate`, `afterUpdate`. By default this method method updates a single document. Set the `multi` mongodb option to update all documents that match the query criteria.
9
9
 
10
10
  ### Arguments
11
11
 
12
12
  `options` *(object)*
13
13
 
14
- - `options.query` *(object\|id)*
15
- - `options.data` *(object)* - data that's validated against the model schema and then wrapped in `{ $set: .. }`, [`more below`](#data)
16
- - [`options.skipValidation`] (string\|array): skip validation for this field name(s)
17
- - [`options.sort`] *(string\|object\|array)*: same as the mongodb option, but allows for string parsing e.g. 'name', 'name:1'
18
- - [`options.timestamps`] *(boolean)*: whether `updatedAt` is automatically updated, defaults to the `manager.timestamps` value
19
- - [`options.blacklist`] *(array\|string\|false)*: augment `schema.updateBL`. `false` will remove all blacklisting
14
+ - `query` *(object\|id)*
15
+ - [`data`](#data) *(object)* - data that's validated against the model fields (always wrapped in `{ $set: .. }`)
16
+ - [[`blacklist`](#blacklisting)]*(array\|string\|false)*: augment `definition.updateBL`. `false` will remove all blacklisting
17
+ - [`project`] *(string\|array\|object)*: project these fields, ignores blacklisting
18
+ - [`skipValidation`] (string\|array): skip validation for this field name(s)
19
+ - [`sort`] *(string\|object\|array)*: same as the mongodb option, but allows for string parsing e.g. 'name', 'name:1'
20
+ - [`timestamps`] *(boolean)*: whether `updatedAt` is automatically updated, defaults to the `manager.timestamps` value
20
21
  - [[`any mongodb option`](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#update)] *(any)*
21
22
 
22
23
  [`callback`] *(function)*: pass instead of return a promise
@@ -33,7 +34,7 @@ user.update({ query: { name: 'foo' }, data: { name: 'bar' }})
33
34
 
34
35
  ### Data
35
36
 
36
- Data that is validated against the model schema and then wrapped in `{ $set: .. }`. Key names can be in dot or bracket notation which is handy for HTML FormData.
37
+ Data that's validated against the model fields (always wrapped in `{ $set: .. }`). Key names can be in dot or bracket notation which is handy for HTML FormData.
37
38
 
38
39
  You can also pass `options.$set` or any other mongodb update operation instead of `options.data`, which bypasses validation, e.g.
39
40
 
@@ -48,10 +49,10 @@ user.update({ query: {}, $pull: { name: 'Martin', badField: 1 }})
48
49
 
49
50
  ### Blacklisting
50
51
 
51
- You can augment the model's `schema.updateBL` blacklist by passing a custom `blacklist`:
52
+ You can augment the model's blacklist (`updateBL`) by passing a custom `blacklist`:
52
53
 
53
54
  ```js
54
55
  // Prevents `name` and `pets.$.name` (array) from being returned.
55
56
  user.update({ query: {}, data: {}, blacklist: ['name', 'pets.name'] })
56
- // You can also whitelist any blacklisted fields found in schema.updateBL
57
+ // You can also whitelist any blacklisted fields found in updateBL
57
58
  user.update({ query: {}, data: {}, blacklist: ['-name', '-pet'] })
@@ -5,7 +5,7 @@ parent: Model
5
5
 
6
6
  # `model.validate`
7
7
 
8
- Validate a model and call related hook: `schema.beforeValidate`
8
+ Validate a model and calls the model hook: `beforeValidate`
9
9
 
10
10
 
11
11
  ### Arguments
@@ -14,10 +14,11 @@ Validate a model and call related hook: `schema.beforeValidate`
14
14
 
15
15
  [`options`] *(object)*
16
16
 
17
- - [`options.skipValidation`] (string\|array): skip validation for this field name(s)
18
- - [`options.blacklist`] *(array\|string\|false)*: augment the model's blacklist. `false` will remove all blacklisting
19
- - [`options.timestamps`] *(boolean)*: whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is updated, depending on the `options.update` value. Defaults to the `manager.timestamps` value
20
- - [`options.update`] *(boolean)*: If true, required rules will be skipped, defaults to false
17
+ - [`skipValidation`] (string\|array): skip validation for this field name(s)
18
+ - [[`blacklist`](#blacklisting)] *(array\|string\|false)*: augment the model's blacklist. `false` will remove all blacklisting
19
+ - [`project`] *(string\|array\|object)*: project these fields, ignores blacklisting
20
+ - [`timestamps`] *(boolean)*: whether `createdAt` and `updatedAt` are inserted, or `updatedAt` is updated, depending on the `update` value. Defaults to the `manager.timestamps` value
21
+ - [`update`] *(boolean)*: If true, required rules will be skipped, defaults to false
21
22
 
22
23
  ### Returns
23
24
 
@@ -34,39 +35,35 @@ db.model('user', {
34
35
  }
35
36
  })
36
37
 
37
- db.user.validate({
38
+ await db.user.validate({
38
39
  name: 'Martin Luther',
39
40
  unknownField: 'Some data'
40
-
41
- }).then(data => {
42
- // { name: "Martin Luther" }
43
41
  })
42
+ // { name: "Martin Luther" }
44
43
 
45
- db.user.validate({
44
+ await db.user.validate({
46
45
  name: 'Martin Luther'
47
46
  address: { city: 'Eisleben' }
48
-
49
- }).catch(errs => {
50
- // [{
51
- // detail: "Value needs to be at least 10 characters long.",
52
- // status: "400",
53
- // title: "address.city",
54
- // meta: {
55
- // field: "city",
56
- // model: "user",
57
- // rule: "minLength"
58
- // }
59
- // }]
60
47
  })
61
-
48
+ // Error [{
49
+ // detail: "Value needs to be at least 10 characters long.",
50
+ // status: "400",
51
+ // title: "address.city",
52
+ // meta: {
53
+ // field: "city",
54
+ // model: "user",
55
+ // rule: "minLength"
56
+ // }
57
+ // }]
62
58
  ```
59
+
63
60
  ### Blacklisting
64
61
 
65
- Depending on the `update` option, you can augment the model's `schema.insertBL` or `schema.updateBL` by passing a custom `blacklist`:
62
+ Depending on the `update` option, you can augment the model's `insertBL` or `updateBL` by passing a custom `blacklist`:
66
63
 
67
64
  ```js
68
65
  // Prevents `name` and `pets.$.name` (array) from being returned.
69
66
  user.validate({}, { blacklist: ['name', 'pets.name'] })
70
- // You can also whitelist any blacklisted fields found in schema's blacklist
67
+ // You can also whitelist any blacklisted fields found in insertBL/updateBL
71
68
  user.validate({}, { blacklist: ['-name', '-pet'] })
72
69
  ```
package/docs/readme.md CHANGED
@@ -6,8 +6,8 @@
6
6
 
7
7
  * User friendly API design, built around the awesome [Monk](https://automattic.github.io/monk/)
8
8
  * Simple CRUD operations with model population
9
- * Model validation deriving from your schema
10
- * Custom error messages can be defined in your schema
9
+ * Model validation deriving from your model definitions
10
+ * Custom error messages can be defined in your model definition
11
11
  * Normalised error responses ready for client consumption
12
12
  * Automatic mongodb index setup
13
13
 
@@ -83,15 +83,16 @@ Coming soon...
83
83
 
84
84
  ## Roadmap
85
85
 
86
+ - Add Aggregate
86
87
  - ~~Add FindOneAndUpdate~~
87
- - Add before/afterInsertUpdate
88
+ - ~~Add beforeInsertUpdate / afterInsertUpdate~~
88
89
  - Bug: Setting an object literal on an ID field ('model') saves successfully
89
90
  - Population within array items
90
91
  - ~~Blacklist false removes all blacklisting~~
91
92
  - ~~Add project to insert/update/validate~~
92
93
  - ~~Whitelisting a parent will remove any previously blacklisted children~~
93
94
  - ~~Blacklist/project works the same across find/insert/update/validate~~
94
- - Automatic subdocument ids
95
+ - Automatic embedded document ids/createdAt/updatedAt fields
95
96
  - Remove ACL default 'public read'
96
97
  - ~~Public db.arrayWithSchema method~~
97
98
  - Global after/before hooks
@@ -245,7 +245,7 @@ module.exports = {
245
245
  if (typeof value === 'undefined' && (!validateUndefined || !rule.validateUndefined)) return
246
246
 
247
247
  // Ignore null (if nullObject is set on objects or arrays)
248
- if (value === null && (field.isObject || field.isArray) && field.nullObject) return
248
+ if (value === null && (field.isObject || field.isArray) && field.nullObject && !rule.validateNull) return
249
249
 
250
250
  // Ignore null
251
251
  if (value === null && !(field.isObject || field.isArray) && !rule.validateNull) return
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.36.1",
5
+ "version": "1.36.2",
6
6
  "license": "MIT",
7
7
  "repository": "github:boycce/monastery",
8
8
  "homepage": "https://boycce.github.io/monastery/",
package/test/crud.js CHANGED
@@ -335,7 +335,7 @@ module.exports = function(monastery, opendb) {
335
335
 
336
336
  test('find default field population', async () => {
337
337
  let db = (await opendb(null)).db
338
- let user = db.model('user', {
338
+ db.model('user', {
339
339
  fields: {
340
340
  name: { type: 'string', default: 'Martin Luther' },
341
341
  addresses: [{ city: { type: 'string' }, country: { type: 'string', default: 'Germany' } }],
@@ -344,7 +344,7 @@ module.exports = function(monastery, opendb) {
344
344
  dogs: [{ model: 'dog' }], // virtual association
345
345
  }
346
346
  })
347
- let dog = db.model('dog', {
347
+ db.model('dog', {
348
348
  fields: {
349
349
  name: { type: 'string', default: 'Scruff' },
350
350
  user: { model: 'user' }
@@ -352,35 +352,35 @@ module.exports = function(monastery, opendb) {
352
352
  })
353
353
 
354
354
  // Default field doesn't override null
355
- let nulldoc1 = await dog.insert({ data: { name: null }})
356
- let nullfind1 = await dog.findOne({ query: nulldoc1._id })
355
+ let nulldoc1 = await db.dog.insert({ data: { name: null }})
356
+ let nullfind1 = await db.dog.findOne({ query: nulldoc1._id })
357
357
  expect(nullfind1).toEqual({ _id: nulldoc1._id, name: null })
358
358
 
359
359
  // Default field doesn't override empty string
360
- let nulldoc2 = await dog.insert({ data: { name: '' }})
361
- let nullfind2 = await dog.findOne({ query: nulldoc2._id })
360
+ let nulldoc2 = await db.dog.insert({ data: { name: '' }})
361
+ let nullfind2 = await db.dog.findOne({ query: nulldoc2._id })
362
362
  expect(nullfind2).toEqual({ _id: nulldoc2._id, name: '' })
363
363
 
364
364
  // Default field overrides undefined
365
- let nulldoc3 = await dog.insert({ data: { name: undefined }})
366
- let nullfind3 = await dog.findOne({ query: nulldoc3._id })
365
+ let nulldoc3 = await db.dog.insert({ data: { name: undefined }})
366
+ let nullfind3 = await db.dog.findOne({ query: nulldoc3._id })
367
367
  expect(nullfind3).toEqual({ _id: nullfind3._id, name: 'Scruff' })
368
368
 
369
369
  // Default field population test
370
- // Note that addresses.1.country shouldn't be overriden
370
+ // Note that addresses.1.country shouldn't be overridden
371
371
  // Insert documents (without defaults)
372
- let inserted = await dog._insert({})
373
- let inserted2 = await user._insert({
372
+ let dog1 = await db.dog._insert({})
373
+ let user1 = await db.user._insert({
374
374
  addresses: [
375
375
  { city: 'Frankfurt' },
376
376
  { city: 'Christchurch', country: 'New Zealand' }
377
377
  ],
378
- pet: { dog: inserted._id }
378
+ pet: { dog: dog1._id }
379
379
  })
380
- await dog._update(inserted._id, { $set: { user: inserted2._id }})
380
+ await db.dog._update(dog1._id, { $set: { user: user1._id }})
381
381
 
382
- let find1 = await user.findOne({
383
- query: inserted2._id,
382
+ let find1 = await db.user.findOne({
383
+ query: user1._id,
384
384
  populate: ['pet.dog', {
385
385
  from: 'dog',
386
386
  localField: '_id',
@@ -389,20 +389,20 @@ module.exports = function(monastery, opendb) {
389
389
  }]
390
390
  })
391
391
  expect(find1).toEqual({
392
- _id: inserted2._id,
392
+ _id: user1._id,
393
393
  name: 'Martin Luther',
394
394
  addresses: [
395
395
  { city: 'Frankfurt', country: 'Germany' },
396
396
  { city: 'Christchurch', country: 'New Zealand' }
397
397
  ],
398
398
  address: { country: 'Germany' },
399
- pet: { dog: { _id: inserted._id, name: 'Scruff', user: inserted2._id }},
400
- dogs: [{ _id: inserted._id, name: 'Scruff', user: inserted2._id }]
399
+ pet: { dog: { _id: dog1._id, name: 'Scruff', user: user1._id }},
400
+ dogs: [{ _id: dog1._id, name: 'Scruff', user: user1._id }]
401
401
  })
402
402
 
403
403
  // Blacklisted default field population test
404
- let find2 = await user.findOne({
405
- query: inserted2._id,
404
+ let find2 = await db.user.findOne({
405
+ query: user1._id,
406
406
  populate: ['pet.dog', {
407
407
  from: 'dog',
408
408
  localField: '_id',
@@ -413,11 +413,11 @@ module.exports = function(monastery, opendb) {
413
413
  // ^ great test (address should cancel addresses if not stopping at the .)
414
414
  })
415
415
  expect(find2).toEqual({
416
- _id: inserted2._id,
416
+ _id: user1._id,
417
417
  name: 'Martin Luther',
418
418
  addresses: [{ city: 'Frankfurt' }, { city: 'Christchurch' }],
419
- pet: { dog: { _id: inserted._id, name: 'Scruff', user: inserted2._id }},
420
- dogs: [{ _id: inserted._id, user: inserted2._id }]
419
+ pet: { dog: { _id: dog1._id, name: 'Scruff', user: user1._id }},
420
+ dogs: [{ _id: dog1._id, user: user1._id }]
421
421
  })
422
422
 
423
423
  db.close()
package/test/test.js CHANGED
@@ -12,6 +12,8 @@
12
12
  * - expect.objectContaining:
13
13
  */
14
14
 
15
+ global.oid = require('mongodb').ObjectID
16
+
15
17
  let monastery = require('../lib')
16
18
  let opendb = async function(uri, opts) {
17
19
  let db = monastery(
package/test/validate.js CHANGED
@@ -808,8 +808,10 @@ module.exports = function(monastery, opendb) {
808
808
  })
809
809
  })
810
810
 
811
- test('schema default rules', async () => {
811
+ test('schema options default', async () => {
812
812
  // Setup
813
+ // todo: test objects
814
+ // todo: change test name
813
815
  let db = (await opendb(false)).db
814
816
  let user = db.model('user', { fields: {
815
817
  name: { type: 'string', minLength: 7 },
@@ -889,7 +891,35 @@ module.exports = function(monastery, opendb) {
889
891
  await expect(user.validate({ amount: 'bad' })).rejects.toContainEqual(mock2)
890
892
  })
891
893
 
892
- test('schema default objects', async () => {
894
+ test('schema options objects', async () => {
895
+ let db = (await opendb(null, {
896
+ timestamps: false,
897
+ nullObjects: true,
898
+ serverSelectionTimeoutMS: 2000
899
+ })).db
900
+ let user = db.model('user', {
901
+ fields: {
902
+ location: {
903
+ lat: { type: 'number' },
904
+ lng: { type: 'number' },
905
+ schema: { required: true },
906
+ },
907
+ },
908
+ })
909
+
910
+ // Subdocument data (null/string)
911
+ await expect(user.validate({ location: null })).rejects.toEqual([{
912
+ 'detail': 'This field is required.',
913
+ 'meta': {'detailLong': undefined, 'field': 'location', 'model': 'user', 'rule': 'required'},
914
+ 'status': '400',
915
+ 'title': 'location'
916
+ }])
917
+ await expect(user.validate({ location: {} })).resolves.toEqual({ location: {} })
918
+
919
+ db.close()
920
+ })
921
+
922
+ test('validate defaultObjects', async () => {
893
923
  let db = (await opendb(null, {
894
924
  timestamps: false,
895
925
  defaultObjects: true,
@@ -915,7 +945,7 @@ module.exports = function(monastery, opendb) {
915
945
  db.close()
916
946
  })
917
947
 
918
- test('schema nullObjects', async () => {
948
+ test('validate nullObjects', async () => {
919
949
  let db = (await opendb(null, {
920
950
  timestamps: false,
921
951
  nullObjects: true,