monastery 1.40.2 → 1.40.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +2 -0
- package/docs/readme.md +6 -0
- package/lib/index.js +4 -1
- package/lib/model-crud.js +33 -3
- package/package.json +1 -1
- package/test/crud.js +22 -7
- package/test/populate.js +72 -0
package/changelog.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [1.40.3](https://github.com/boycce/monastery/compare/1.40.2...1.40.3) (2022-12-21)
|
|
6
|
+
|
|
5
7
|
### [1.40.2](https://github.com/boycce/monastery/compare/1.40.1...1.40.2) (2022-12-15)
|
|
6
8
|
|
|
7
9
|
### [1.40.1](https://github.com/boycce/monastery/compare/1.40.0...1.40.1) (2022-12-14)
|
package/docs/readme.md
CHANGED
|
@@ -95,11 +95,17 @@ Coming soon...
|
|
|
95
95
|
- Automatic embedded document ids/createdAt/updatedAt fields
|
|
96
96
|
- ~~Ability to change ACL default on the manager~~
|
|
97
97
|
- ~~Public db.arrayWithSchema method~~
|
|
98
|
+
- ~~Added support for array population~~
|
|
99
|
+
- Change population warnings into errors
|
|
98
100
|
- Global after/before hooks
|
|
99
101
|
- before hooks can receive a data array, remove this
|
|
100
102
|
- docs: Make the implicit ID query conversion more apparent
|
|
101
103
|
- Split away from Monk (unless updated)
|
|
102
104
|
- Add a warning if an invalid model is referenced in jthe schema
|
|
105
|
+
- Allow rules on image types, e.g. `required`
|
|
106
|
+
- test importing of models
|
|
107
|
+
- Docs: model.methods
|
|
108
|
+
-
|
|
103
109
|
|
|
104
110
|
## Versions
|
|
105
111
|
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
let fs = require('fs')
|
|
2
2
|
let debug = require('debug')
|
|
3
3
|
let monk = require('monk')
|
|
4
|
-
let util = require('./util')
|
|
5
4
|
let path = require('path')
|
|
5
|
+
let rules = require('./rules')
|
|
6
|
+
let util = require('./util')
|
|
6
7
|
|
|
7
8
|
// Apply monk monkey patches
|
|
8
9
|
monk.manager.prototype.open = require('./monk-monkey-patches').open
|
|
@@ -72,6 +73,8 @@ module.exports = function(uri, opts, fn) {
|
|
|
72
73
|
return manager
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
module.exports.rules = rules
|
|
77
|
+
|
|
75
78
|
let arrayWithSchema = function(array, schema) {
|
|
76
79
|
array.schema = schema
|
|
77
80
|
return array
|
package/lib/model-crud.js
CHANGED
|
@@ -89,13 +89,37 @@ module.exports = {
|
|
|
89
89
|
if (util.isObject(item)) {
|
|
90
90
|
lookups.push({ $lookup: item })
|
|
91
91
|
} else {
|
|
92
|
-
let
|
|
92
|
+
let arrayTarget
|
|
93
|
+
let arrayCount = 0
|
|
94
|
+
let schema = path.split('.').reduce((o, i) => {
|
|
95
|
+
if (util.isArray(o[i])) {
|
|
96
|
+
arrayCount++
|
|
97
|
+
arrayTarget = true
|
|
98
|
+
return o[i][0]
|
|
99
|
+
} else {
|
|
100
|
+
arrayTarget = false
|
|
101
|
+
return o[i]
|
|
102
|
+
}
|
|
103
|
+
}, this.fields)
|
|
104
|
+
let modelName = (schema||{}).model
|
|
93
105
|
if (!modelName) {
|
|
94
106
|
this.error(
|
|
95
107
|
`The field "${path}" passed to populate is not of type model. You would ` +
|
|
96
108
|
'need to add the field option e.g. { model: \'comment\' } in your schema.'
|
|
97
109
|
)
|
|
98
110
|
continue
|
|
111
|
+
} else if (arrayCount > 1) {
|
|
112
|
+
this.error(
|
|
113
|
+
`You cannot populate on array's nested in array's: ${path}: ` +
|
|
114
|
+
`{ model: "${modelName}" }`
|
|
115
|
+
)
|
|
116
|
+
continue
|
|
117
|
+
} else if (arrayCount == 1 && !arrayTarget) {
|
|
118
|
+
this.error(
|
|
119
|
+
`You cannot populate within an array of sub-documents: ${path}: ` +
|
|
120
|
+
`{ model: "${modelName}" }`
|
|
121
|
+
)
|
|
122
|
+
continue
|
|
99
123
|
} else if (!this.manager.model[modelName]) {
|
|
100
124
|
this.error(
|
|
101
125
|
`The field's model defined in your schema does not exist: ${path}: ` +
|
|
@@ -103,8 +127,11 @@ module.exports = {
|
|
|
103
127
|
)
|
|
104
128
|
continue
|
|
105
129
|
}
|
|
106
|
-
//
|
|
107
|
-
(
|
|
130
|
+
// Convert array into a document for non-array targets
|
|
131
|
+
if (!arrayTarget) {
|
|
132
|
+
(opts.addFields = opts.addFields || {})[path] = { '$arrayElemAt': [ '$' + path, 0 ] }
|
|
133
|
+
}
|
|
134
|
+
// Create lookup
|
|
108
135
|
lookups.push({ $lookup: {
|
|
109
136
|
from: modelName,
|
|
110
137
|
localField: path,
|
|
@@ -502,6 +529,9 @@ module.exports = {
|
|
|
502
529
|
let parentModelData = util.toArray(data).map(o => ({ modelName: this.name, dataRef: o }))
|
|
503
530
|
let modelData = this._recurseAndFindModels(this.fields, data).concat(parentModelData)
|
|
504
531
|
|
|
532
|
+
// Need to remove ////////////////// make sure blacklist default fields on populated arrays dont show
|
|
533
|
+
console.log(modelData)
|
|
534
|
+
|
|
505
535
|
// Loop found model/deep-model data objects, and populate missing default-fields and call afterFind on each
|
|
506
536
|
for (let item of modelData) {
|
|
507
537
|
// Populate missing default fields if data !== null
|
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.40.
|
|
5
|
+
"version": "1.40.3",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/test/crud.js
CHANGED
|
@@ -460,6 +460,7 @@ module.exports = function(monastery, opendb) {
|
|
|
460
460
|
addresses: [{ city: { type: 'string' }, country: { type: 'string', default: 'Germany' } }],
|
|
461
461
|
address: { country: { type: 'string', default: 'Germany' }},
|
|
462
462
|
pet: { dog: { model: 'dog' }},
|
|
463
|
+
pets: { dog: [{ model: 'dog' }]},
|
|
463
464
|
dogs: [{ model: 'dog' }], // virtual association
|
|
464
465
|
}
|
|
465
466
|
})
|
|
@@ -489,18 +490,20 @@ module.exports = function(monastery, opendb) {
|
|
|
489
490
|
// Note that addresses.1.country shouldn't be overridden
|
|
490
491
|
// Insert documents (without defaults)
|
|
491
492
|
let dog1 = await db.dog._insert({})
|
|
493
|
+
let dog2 = await db.dog._insert({})
|
|
492
494
|
let user1 = await db.user._insert({
|
|
493
495
|
addresses: [
|
|
494
496
|
{ city: 'Frankfurt' },
|
|
495
497
|
{ city: 'Christchurch', country: 'New Zealand' }
|
|
496
498
|
],
|
|
497
|
-
pet: { dog: dog1._id }
|
|
499
|
+
pet: { dog: dog1._id },
|
|
500
|
+
pets: { dog: [dog1._id, dog2._id]},
|
|
498
501
|
})
|
|
499
502
|
await db.dog._update(dog1._id, { $set: { user: user1._id }})
|
|
500
503
|
|
|
501
504
|
let find1 = await db.user.findOne({
|
|
502
505
|
query: user1._id,
|
|
503
|
-
populate: ['pet.dog', {
|
|
506
|
+
populate: ['pet.dog', 'pets.dog', {
|
|
504
507
|
from: 'dog',
|
|
505
508
|
localField: '_id',
|
|
506
509
|
foreignField: 'user',
|
|
@@ -516,19 +519,25 @@ module.exports = function(monastery, opendb) {
|
|
|
516
519
|
],
|
|
517
520
|
address: { country: 'Germany' },
|
|
518
521
|
pet: { dog: { _id: dog1._id, name: 'Scruff', user: user1._id }},
|
|
522
|
+
pets: {
|
|
523
|
+
dog: [
|
|
524
|
+
{ _id: dog1._id, name: 'Scruff', user: user1._id },
|
|
525
|
+
{ _id: dog2._id, name: 'Scruff' },
|
|
526
|
+
]
|
|
527
|
+
},
|
|
519
528
|
dogs: [{ _id: dog1._id, name: 'Scruff', user: user1._id }]
|
|
520
529
|
})
|
|
521
530
|
|
|
522
531
|
// Blacklisted default field population test
|
|
523
532
|
let find2 = await db.user.findOne({
|
|
524
533
|
query: user1._id,
|
|
525
|
-
populate: ['pet.dog', {
|
|
534
|
+
populate: ['pet.dog', 'pets.dog', {
|
|
526
535
|
from: 'dog',
|
|
527
536
|
localField: '_id',
|
|
528
537
|
foreignField: 'user',
|
|
529
|
-
as: 'dogs'
|
|
538
|
+
as: 'dogs',
|
|
530
539
|
}],
|
|
531
|
-
blacklist: ['address', 'addresses.country', 'dogs.name']
|
|
540
|
+
blacklist: ['address', 'addresses.country', 'pets.dog.name', 'dogs.name'],
|
|
532
541
|
// ^ great test (address should cancel addresses if not stopping at the .)
|
|
533
542
|
})
|
|
534
543
|
expect(find2).toEqual({
|
|
@@ -536,14 +545,20 @@ module.exports = function(monastery, opendb) {
|
|
|
536
545
|
name: 'Martin Luther',
|
|
537
546
|
addresses: [{ city: 'Frankfurt' }, { city: 'Christchurch' }],
|
|
538
547
|
pet: { dog: { _id: dog1._id, name: 'Scruff', user: user1._id }},
|
|
539
|
-
dogs: [{ _id: dog1._id, user: user1._id }]
|
|
548
|
+
dogs: [{ _id: dog1._id, user: user1._id }],
|
|
549
|
+
pets: {
|
|
550
|
+
dog: [
|
|
551
|
+
{ _id: dog1._id, user: user1._id }, //////////////////get this working
|
|
552
|
+
{ _id: dog2._id },
|
|
553
|
+
]
|
|
554
|
+
},
|
|
540
555
|
})
|
|
541
556
|
|
|
542
557
|
db.close()
|
|
543
558
|
})
|
|
544
559
|
|
|
545
560
|
test('findOneAndUpdate general', async () => {
|
|
546
|
-
// todo: test all findOneAndUpdate options
|
|
561
|
+
// todo: test all findOneAndUpdate options (e.g. array population)
|
|
547
562
|
// todo: test find & update hooks
|
|
548
563
|
let db = (await opendb(null)).db
|
|
549
564
|
let dog = db.model('dog', {
|
package/test/populate.js
CHANGED
|
@@ -82,6 +82,78 @@ module.exports = function(monastery, opendb) {
|
|
|
82
82
|
db.close()
|
|
83
83
|
})
|
|
84
84
|
|
|
85
|
+
test('model populate array', async () => {
|
|
86
|
+
// Setup
|
|
87
|
+
let db = (await opendb(null)).db
|
|
88
|
+
let bird = db.model('bird', { fields: {
|
|
89
|
+
name: { type: 'string' }
|
|
90
|
+
}})
|
|
91
|
+
let user = db.model('user', { fields: {
|
|
92
|
+
birds: [{ model: 'bird' }],
|
|
93
|
+
animal: { birds: [{ model: 'bird' }] },
|
|
94
|
+
animals: [{ bird: { model: 'bird' }, num: { type: 'number' } }],
|
|
95
|
+
}})
|
|
96
|
+
let bird1 = await bird.insert({ data: { name: 'ponyo' }})
|
|
97
|
+
let bird2 = await bird.insert({ data: { name: 'jack' }})
|
|
98
|
+
let bird3 = await bird.insert({ data: { name: 'sophie' }})
|
|
99
|
+
let user1 = await user.insert({ data: {
|
|
100
|
+
birds: [bird1._id, bird2._id],
|
|
101
|
+
animal: { birds: [bird1._id, bird2._id] },
|
|
102
|
+
animals: [{ bird: bird1._id, num: 1 }, { bird: bird3._id, num: 2 }],
|
|
103
|
+
}})
|
|
104
|
+
|
|
105
|
+
// Array
|
|
106
|
+
let find1 = await user.findOne({ query: user1._id, populate: ['birds'] })
|
|
107
|
+
expect(find1).toEqual({
|
|
108
|
+
_id: user1._id,
|
|
109
|
+
birds: [
|
|
110
|
+
{ _id: bird1._id, name: 'ponyo' },
|
|
111
|
+
{ _id: bird2._id, name: 'jack' },
|
|
112
|
+
],
|
|
113
|
+
animal: {
|
|
114
|
+
birds: [bird1._id, bird2._id],
|
|
115
|
+
},
|
|
116
|
+
animals: [
|
|
117
|
+
{ bird: bird1._id, num: 1 },
|
|
118
|
+
{ bird: bird3._id, num: 2 },
|
|
119
|
+
],
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// Nested array
|
|
123
|
+
let find2 = await user.findOne({ query: user1._id, populate: ['animal.birds'] })
|
|
124
|
+
expect(find2).toEqual({
|
|
125
|
+
_id: user1._id,
|
|
126
|
+
birds: [bird1._id, bird2._id],
|
|
127
|
+
animal: {
|
|
128
|
+
birds: [
|
|
129
|
+
{ _id: bird1._id, name: 'ponyo' },
|
|
130
|
+
{ _id: bird2._id, name: 'jack' },
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
animals: [
|
|
134
|
+
{ bird: bird1._id, num: 1 },
|
|
135
|
+
{ bird: bird3._id, num: 2 },
|
|
136
|
+
],
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
// modelId within an array of subdocuments (won't populate, but show a debug error)
|
|
140
|
+
user.error = () => {} // hide debug error
|
|
141
|
+
let find3 = await user.findOne({ query: user1._id, populate: ['animals.bird'] })
|
|
142
|
+
expect(find3).toEqual({
|
|
143
|
+
_id: user1._id,
|
|
144
|
+
birds: [bird1._id, bird2._id],
|
|
145
|
+
animal: {
|
|
146
|
+
birds: [bird1._id, bird2._id],
|
|
147
|
+
},
|
|
148
|
+
animals: [
|
|
149
|
+
{ bird: bird1._id, num: 1 },
|
|
150
|
+
{ bird: bird3._id, num: 2 },
|
|
151
|
+
],
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
db.close()
|
|
155
|
+
})
|
|
156
|
+
|
|
85
157
|
test('model populate type=any', async () => {
|
|
86
158
|
let db = (await opendb(null)).db
|
|
87
159
|
db.model('company', { fields: {
|