monastery 3.4.2 → 3.4.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/manager/model.md +3 -1
- package/docs/manager/models.md +2 -2
- package/lib/index.js +1 -1
- package/lib/model.js +32 -31
- package/package.json +1 -1
- package/test/crud.js +6 -5
- package/test/model.js +3 -6
- package/test/plugin-images.js +17 -18
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
|
+
### [3.4.3](https://github.com/boycce/monastery/compare/3.4.2...3.4.3) (2024-08-27)
|
|
6
|
+
|
|
5
7
|
### [3.4.2](https://github.com/boycce/monastery/compare/3.4.1...3.4.2) (2024-08-24)
|
|
6
8
|
|
|
7
9
|
### [3.4.1](https://github.com/boycce/monastery/compare/3.4.0...3.4.1) (2024-08-09)
|
package/docs/manager/model.md
CHANGED
|
@@ -11,7 +11,9 @@ Sets up a model, retrieves a collection, and sets up any required indexes
|
|
|
11
11
|
|
|
12
12
|
`name` *(string)*: name of the mongo collection
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
`definition` *(object)*: [definition](../definition)
|
|
15
|
+
|
|
16
|
+
`waitForIndexes` *(boolean)*: wait for indexes to be setup (returns a promise instead of a model)
|
|
15
17
|
|
|
16
18
|
### Returns
|
|
17
19
|
|
package/docs/manager/models.md
CHANGED
|
@@ -13,7 +13,7 @@ Setup model definitions from a folder location
|
|
|
13
13
|
|
|
14
14
|
[`options`] *(object)*:
|
|
15
15
|
- [`commonJs=false`] *(boolean)*: for old commonjs projects, you will need to set this to `true` which uses `require` instead of `import` (removed in `3.0.0`)
|
|
16
|
-
- [`waitForIndexes=false`] *(boolean)*:
|
|
16
|
+
- [`waitForIndexes=false`] *(boolean)*: wait for collection indexes to be setup
|
|
17
17
|
|
|
18
18
|
### Returns
|
|
19
19
|
|
|
@@ -39,5 +39,5 @@ export default { // Make sure the model definition is exported as the default
|
|
|
39
39
|
```
|
|
40
40
|
|
|
41
41
|
```js
|
|
42
|
-
await db.models(__dirname +
|
|
42
|
+
await db.models(__dirname + 'models', { waitForIndexes: true })
|
|
43
43
|
```
|
package/lib/index.js
CHANGED
|
@@ -211,7 +211,7 @@ Manager.prototype.models = async function(pathname, opts) {
|
|
|
211
211
|
if (definition && definition.default) definition = definition.default
|
|
212
212
|
if (!definition) throw new Error(`The model definition for '${name}' must export a default object`)
|
|
213
213
|
// Wait for indexes to be created?
|
|
214
|
-
if (waitForIndexes) out[name] = await this.model(name,
|
|
214
|
+
if (waitForIndexes) out[name] = await this.model(name, definition, waitForIndexes)
|
|
215
215
|
else out[name] = this.model(name, definition)
|
|
216
216
|
}
|
|
217
217
|
return out
|
package/lib/model.js
CHANGED
|
@@ -1,58 +1,59 @@
|
|
|
1
1
|
const rules = require('./rules.js')
|
|
2
2
|
const util = require('./util.js')
|
|
3
3
|
|
|
4
|
-
function Model(name,
|
|
4
|
+
function Model(name, definition, waitForIndexes, manager) {
|
|
5
5
|
/**
|
|
6
6
|
* Setup a model
|
|
7
7
|
* @param {string} name
|
|
8
|
-
* @param {object}
|
|
9
|
-
* @param {boolean}
|
|
10
|
-
*
|
|
11
|
-
* @
|
|
8
|
+
* @param {object} definition - model definition
|
|
9
|
+
* @param {boolean} waitForIndexes - wait for indexes to be created before returning (returns a promise)
|
|
10
|
+
*
|
|
11
|
+
* @return model or Promise(model)
|
|
12
|
+
* @this manager
|
|
12
13
|
*/
|
|
13
14
|
if (!(this instanceof Model)) {
|
|
14
|
-
return new Model(name,
|
|
15
|
+
return new Model(name, definition, waitForIndexes, this)
|
|
15
16
|
} else if (!name) {
|
|
16
17
|
throw 'No model name defined'
|
|
17
18
|
} else if (name.match(/^_/)) {
|
|
18
19
|
throw 'Model names cannot start with an underscore'
|
|
19
|
-
} else if (!
|
|
20
|
+
} else if (!definition) {
|
|
20
21
|
throw `No model definition passed for "${name}"`
|
|
21
|
-
} else if (!
|
|
22
|
+
} else if (!definition.fields) {
|
|
22
23
|
throw `We couldn't find ${name}.fields in the model definition, the model maybe setup `
|
|
23
|
-
+ `or exported incorrectly:\n${JSON.stringify(
|
|
24
|
-
} else if (!util.isSubdocument(
|
|
24
|
+
+ `or exported incorrectly:\n${JSON.stringify(definition, null, 2)}`
|
|
25
|
+
} else if (!util.isSubdocument(definition.fields) && definition.fields.type == 'any') {
|
|
25
26
|
throw `Instead of using { type: 'any' } for ${name}.fields, please use the new 'strict' definition rule` +
|
|
26
27
|
', e.g. { schema: { strict: false }}'
|
|
27
|
-
} else if (!util.isSubdocument(
|
|
28
|
+
} else if (!util.isSubdocument(definition.fields) && !util.isEmpty(definition.fields)) {
|
|
28
29
|
throw `The ${name}.fields object should be a valid document, e.g. { name: { type: 'string' }}`
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
// Add schema options
|
|
32
33
|
Object.assign(this, {
|
|
33
|
-
...(
|
|
34
|
-
afterFind:
|
|
35
|
-
afterInsert: (
|
|
36
|
-
afterUpdate: (
|
|
37
|
-
afterRemove:
|
|
38
|
-
beforeInsert: (
|
|
39
|
-
beforeUpdate: (
|
|
40
|
-
beforeRemove:
|
|
41
|
-
beforeValidate:
|
|
34
|
+
...(definition.methods || {}),
|
|
35
|
+
afterFind: definition.afterFind || [],
|
|
36
|
+
afterInsert: (definition.afterInsert || []).concat(definition.afterInsertUpdate || []),
|
|
37
|
+
afterUpdate: (definition.afterUpdate || []).concat(definition.afterInsertUpdate || []),
|
|
38
|
+
afterRemove: definition.afterRemove || [],
|
|
39
|
+
beforeInsert: (definition.beforeInsert || []).concat(definition.beforeInsertUpdate || []),
|
|
40
|
+
beforeUpdate: (definition.beforeUpdate || []).concat(definition.beforeInsertUpdate || []),
|
|
41
|
+
beforeRemove: definition.beforeRemove || [],
|
|
42
|
+
beforeValidate: definition.beforeValidate || [],
|
|
42
43
|
error: manager.error,
|
|
43
44
|
info: manager.info,
|
|
44
45
|
warn: manager.warn,
|
|
45
|
-
insertBL:
|
|
46
|
-
? !
|
|
47
|
-
|
|
48
|
-
fields: { ...(util.deepCopy(
|
|
49
|
-
findBL:
|
|
46
|
+
insertBL: !definition.insertBL
|
|
47
|
+
? ['_id'] : !definition.insertBL.includes('_id') && !definition.insertBL.includes('-_id')
|
|
48
|
+
? ['_id'].concat(definition.insertBL) : definition.insertBL,
|
|
49
|
+
fields: { ...(util.deepCopy(definition.fields) || {}) },
|
|
50
|
+
findBL: definition.findBL || ['password'], // todo: password should be removed
|
|
50
51
|
manager: manager,
|
|
51
|
-
messages:
|
|
52
|
-
messagesLen: Object.keys(
|
|
52
|
+
messages: definition.messages || {},
|
|
53
|
+
messagesLen: Object.keys(definition.messages || {}).length > 0,
|
|
53
54
|
name: name,
|
|
54
|
-
rules: { ...(
|
|
55
|
-
updateBL:
|
|
55
|
+
rules: { ...(definition.rules || {}) },
|
|
56
|
+
updateBL: definition.updateBL || [],
|
|
56
57
|
})
|
|
57
58
|
|
|
58
59
|
// Sort messages by specifity first, then we can just return the first match
|
|
@@ -140,7 +141,7 @@ function Model(name, opts, manager) {
|
|
|
140
141
|
if (err.type == 'info') this.info(err.detail)
|
|
141
142
|
else this.error(err)
|
|
142
143
|
}
|
|
143
|
-
if (
|
|
144
|
+
if (waitForIndexes) return this._setupIndexes().catch(errHandler).then(() => this)
|
|
144
145
|
else this._setupIndexes().catch(errHandler) // returns this
|
|
145
146
|
}
|
|
146
147
|
|
|
@@ -446,4 +447,4 @@ module.exports = Model
|
|
|
446
447
|
|
|
447
448
|
// Extend Model prototype
|
|
448
449
|
require('./model-crud.js')
|
|
449
|
-
require('./model-validate.js')
|
|
450
|
+
require('./model-validate.js')
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "monastery",
|
|
3
3
|
"description": "⛪ A simple, straightforward MongoDB ODM",
|
|
4
4
|
"author": "Ricky Boyce",
|
|
5
|
-
"version": "3.4.
|
|
5
|
+
"version": "3.4.3",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/test/crud.js
CHANGED
|
@@ -353,10 +353,7 @@ test('find default field blacklisted', async () => {
|
|
|
353
353
|
|
|
354
354
|
test('find default field population with option noDefaults', async () => {
|
|
355
355
|
async function setup(noDefaults) {
|
|
356
|
-
|
|
357
|
-
* Setup
|
|
358
|
-
* @returns {Object} {dog, user, dog1Doc, dog2Doc, user1Doc}
|
|
359
|
-
*/
|
|
356
|
+
// @returns {Object} {dog, user, dog1Doc, dog2Doc, user1Doc}
|
|
360
357
|
// similar to "find default field population"
|
|
361
358
|
const db = monastery('127.0.0.1/monastery', { noDefaults: noDefaults, timestamps: false })
|
|
362
359
|
const userDefinition = {
|
|
@@ -508,6 +505,9 @@ test('find default field population with option noDefaults', async () => {
|
|
|
508
505
|
},
|
|
509
506
|
dogs: [{ _id: s2.dog1Doc._id, user: s2.user1Doc._id }], // should not have a default name
|
|
510
507
|
})
|
|
508
|
+
|
|
509
|
+
s1.db.close()
|
|
510
|
+
s2.db.close()
|
|
511
511
|
})
|
|
512
512
|
|
|
513
513
|
test('update general', async () => {
|
|
@@ -1074,7 +1074,6 @@ test('hooks > basic', async () => {
|
|
|
1074
1074
|
await expect(user2.update({ query: userDoc2._id, data: { first: 'MM' } })).resolves.toEqual({ first: 'MM' })
|
|
1075
1075
|
await expect(user2.update({ query: userDoc2._id, data: { first: 'M', bad: true } })).rejects.toThrow('error2')
|
|
1076
1076
|
})
|
|
1077
|
-
|
|
1078
1077
|
test('hooks > chained values', async () => {
|
|
1079
1078
|
let bookCount = 0
|
|
1080
1079
|
const afterInsertAsync = [
|
|
@@ -1514,4 +1513,6 @@ test('update set and unset with option skipValidation', async () => {
|
|
|
1514
1513
|
const u8 = { query: userId, $unset: { 'profile.name': '' }, skipValidation: true }
|
|
1515
1514
|
await expect(user.update(u8)).resolves.toEqual({})
|
|
1516
1515
|
await expect(user.findOne(userId)).resolves.toEqual({ _id: userId, profile: {} })
|
|
1516
|
+
|
|
1517
|
+
db2.close()
|
|
1517
1518
|
})
|
package/test/model.js
CHANGED
|
@@ -449,12 +449,11 @@ test('model indexes basic', async () => {
|
|
|
449
449
|
|
|
450
450
|
// Unique & text index
|
|
451
451
|
let userIndexModel = await db.model('userIndex', {
|
|
452
|
-
waitForIndexes: true,
|
|
453
452
|
fields: {
|
|
454
453
|
email: { type: 'string', index: 'unique' },
|
|
455
454
|
name: { type: 'string', index: 'text' },
|
|
456
455
|
},
|
|
457
|
-
})
|
|
456
|
+
}, { waitForIndexes: true })
|
|
458
457
|
|
|
459
458
|
let userIndexModelIndexes = await db.db.collection('userIndex').indexes()
|
|
460
459
|
expect(userIndexModelIndexes[0]).toEqual({
|
|
@@ -504,7 +503,6 @@ test('model indexes unique', async () => {
|
|
|
504
503
|
|
|
505
504
|
// Partial unique indexes (allows mulitple null values)
|
|
506
505
|
await db.model('userUniqueIndex', {
|
|
507
|
-
waitForIndexes: true,
|
|
508
506
|
fields: {
|
|
509
507
|
email: {
|
|
510
508
|
type: 'string',
|
|
@@ -516,7 +514,7 @@ test('model indexes unique', async () => {
|
|
|
516
514
|
},
|
|
517
515
|
},
|
|
518
516
|
},
|
|
519
|
-
})
|
|
517
|
+
}, { waitForIndexes: true })
|
|
520
518
|
|
|
521
519
|
let indexes2 = await db.db.collection('userUniqueIndex').indexes()
|
|
522
520
|
expect(indexes2[0]).toEqual({
|
|
@@ -648,7 +646,6 @@ test('model indexes 2dsphere', async () => {
|
|
|
648
646
|
// Setup. The tested model needs to be unique as race condition issue arises when the same model
|
|
649
647
|
// with text indexes are setup at the same time
|
|
650
648
|
await db.model('user99', {
|
|
651
|
-
waitForIndexes: true,
|
|
652
649
|
fields: {
|
|
653
650
|
location: {
|
|
654
651
|
index: '2dsphere',
|
|
@@ -661,7 +658,7 @@ test('model indexes 2dsphere', async () => {
|
|
|
661
658
|
coordinates: [{ type: 'number' }], // lat, lng
|
|
662
659
|
},
|
|
663
660
|
},
|
|
664
|
-
})
|
|
661
|
+
}, { waitForIndexes: true })
|
|
665
662
|
|
|
666
663
|
// Schema check
|
|
667
664
|
expect(db.user99.fields.location).toEqual({
|
package/test/plugin-images.js
CHANGED
|
@@ -778,27 +778,26 @@ test('images option getSignedUrls', async () => {
|
|
|
778
778
|
})
|
|
779
779
|
|
|
780
780
|
// Find signed URL via query option
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
//
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
781
|
+
await expect(db3.user.findOne({ query: userInserted._id, getSignedUrls: true })).resolves.toEqual({
|
|
782
|
+
_id: expect.any(Object),
|
|
783
|
+
photos: [imageWithSignedUrl, imageWithSignedUrl],
|
|
784
|
+
photos2: [imageWithSignedUrl, imageWithSignedUrl],
|
|
785
|
+
})
|
|
786
|
+
|
|
787
|
+
// Find signed URL via schema option
|
|
788
|
+
await expect(db3.user.findOne({ query: userInserted._id })).resolves.toEqual({
|
|
789
|
+
_id: expect.any(Object),
|
|
790
|
+
photos: [image, image],
|
|
791
|
+
photos2: [imageWithSignedUrl, imageWithSignedUrl],
|
|
792
|
+
})
|
|
793
793
|
|
|
794
794
|
// Works with _processAfterFind
|
|
795
795
|
let rawUser = await db3.user._findOne({ _id: userInserted._id })
|
|
796
|
-
db3.user._processAfterFind(rawUser)
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
// })
|
|
796
|
+
await expect(db3.user._processAfterFind(rawUser)).resolves.toEqual({
|
|
797
|
+
_id: expect.any(Object),
|
|
798
|
+
photos: [image, image],
|
|
799
|
+
photos2: [imageWithSignedUrl, imageWithSignedUrl],
|
|
800
|
+
})
|
|
802
801
|
db3.close()
|
|
803
802
|
})
|
|
804
803
|
|