monastery 3.0.5 → 3.0.6
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 -0
- package/changelog.md +2 -0
- package/docs/readme.md +4 -2
- package/lib/model-crud.js +9 -7
- package/lib/model-validate.js +2 -1
- package/lib/util.js +9 -7
- package/package.json +1 -1
- package/test/crud.js +90 -0
package/.eslintrc.json
CHANGED
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.0.6](https://github.com/boycce/monastery/compare/3.0.5...3.0.6) (2024-04-29)
|
|
6
|
+
|
|
5
7
|
### [3.0.5](https://github.com/boycce/monastery/compare/3.0.4...3.0.5) (2024-04-29)
|
|
6
8
|
|
|
7
9
|
### [3.0.4](https://github.com/boycce/monastery/compare/3.0.3...3.0.4) (2024-04-29)
|
package/docs/readme.md
CHANGED
|
@@ -87,9 +87,11 @@ You can view MongoDB's [compatibility table here](https://www.mongodb.com/docs/d
|
|
|
87
87
|
- Removed callback functions on all model methods, you can use the returned promise instead
|
|
88
88
|
- model.update() now returns the following _update property: `{ acknowledged: true, matchedCount: 1, modifiedCount: 1, upsertedCount: 0, upsertedId: null }` instead of `{ n: 1, nModified: 1, ok: 1 }`
|
|
89
89
|
- model.remove() now returns `{ acknowledged: true, deletedCount: 1 }`, instead of `{ results: {n:1, ok:1} }`
|
|
90
|
-
- Models are now added to db.models instead of db.model, e.g. db.models.user
|
|
91
|
-
- MongoDB connection can be found here db.db changed from db._db
|
|
92
90
|
- model._indexes() now returns collection._indexes() not collection._indexInformation()
|
|
91
|
+
- db.model.* moved to db.models.*
|
|
92
|
+
- db._client moved to db.client
|
|
93
|
+
- db._db moved to db.db
|
|
94
|
+
- db.catch/then() moved to db.onError/db.onOpen()
|
|
93
95
|
|
|
94
96
|
## Roadmap
|
|
95
97
|
|
package/lib/model-crud.js
CHANGED
|
@@ -48,9 +48,9 @@ Model.prototype.insert = async function (opts) {
|
|
|
48
48
|
let data = await this.validate(opts.data || {}, opts) // was { ...opts }
|
|
49
49
|
|
|
50
50
|
// Insert
|
|
51
|
-
await util.runSeries(this.beforeInsert.map(f => f.bind(opts, data)))
|
|
51
|
+
await util.runSeries(this.beforeInsert.map(f => f.bind(opts, data)), `${this.name}.beforeInsert`)
|
|
52
52
|
let response = await this._insert(data, util.omit(opts, this._queryOptions))
|
|
53
|
-
await util.runSeries(this.afterInsert.map(f => f.bind(opts, response)))
|
|
53
|
+
await util.runSeries(this.afterInsert.map(f => f.bind(opts, response)), `${this.name}.afterInsert`)
|
|
54
54
|
|
|
55
55
|
// Success/error
|
|
56
56
|
if (opts.req && opts.respond) opts.req.res.json(response)
|
|
@@ -267,7 +267,7 @@ Model.prototype.update = async function (opts, type='update') {
|
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
// Hook: beforeUpdate (has access to original, non-validated opts.data)
|
|
270
|
-
await util.runSeries(this.beforeUpdate.map(f => f.bind(opts, data||{})))
|
|
270
|
+
await util.runSeries(this.beforeUpdate.map(f => f.bind(opts, data||{})), `${this.name}.beforeUpdate`)
|
|
271
271
|
|
|
272
272
|
if (data && operators['$set']) {
|
|
273
273
|
this.info(`'$set' fields take precedence over the data fields for \`${this.name}.${type}()\``)
|
|
@@ -297,7 +297,9 @@ Model.prototype.update = async function (opts, type='update') {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
// Hook: afterUpdate (doesn't have access to validated data)
|
|
300
|
-
if (response)
|
|
300
|
+
if (response) {
|
|
301
|
+
await util.runSeries(this.afterUpdate.map(f => f.bind(opts, response)), `${this.name}.afterUpdate`)
|
|
302
|
+
}
|
|
301
303
|
|
|
302
304
|
// Hook: afterFind if findOneAndUpdate
|
|
303
305
|
if (response && type == 'findOneAndUpdate') {
|
|
@@ -329,9 +331,9 @@ Model.prototype.remove = async function (opts) {
|
|
|
329
331
|
opts = await this._queryObject(opts, 'remove')
|
|
330
332
|
|
|
331
333
|
// Remove
|
|
332
|
-
await util.runSeries(this.beforeRemove.map(f => f.bind(opts)))
|
|
334
|
+
await util.runSeries(this.beforeRemove.map(f => f.bind(opts)), `${this.name}.beforeRemove`)
|
|
333
335
|
let response = await this._remove(opts.query, util.omit(opts, this._queryOptions))
|
|
334
|
-
await util.runSeries(this.afterRemove.map(f => f.bind(response)))
|
|
336
|
+
await util.runSeries(this.afterRemove.map(f => f.bind(response)), `${this.name}.afterRemove`)
|
|
335
337
|
|
|
336
338
|
// Success
|
|
337
339
|
if (opts.req && opts.respond) opts.req.res.json(response)
|
|
@@ -589,7 +591,7 @@ Model.prototype._processAfterFind = function (data, projection={}, afterFindCont
|
|
|
589
591
|
callbackSeries.push(fn.bind(afterFindContext, item.dataRef))
|
|
590
592
|
}
|
|
591
593
|
}
|
|
592
|
-
return util.runSeries(callbackSeries).then(() => data)
|
|
594
|
+
return util.runSeries(callbackSeries, 'afterFind').then(() => data)
|
|
593
595
|
}
|
|
594
596
|
|
|
595
597
|
Model.prototype._recurseAndFindModels = function (parentPath, schemaFields, dataArr) {
|
package/lib/model-validate.js
CHANGED
|
@@ -29,7 +29,8 @@ Model.prototype.validate = async function (data, opts) {
|
|
|
29
29
|
else opts.projectionValidate = this._getProjectionFromBlacklist(opts.update ? 'update' : 'insert', opts.blacklist)
|
|
30
30
|
|
|
31
31
|
// Hook: beforeValidate
|
|
32
|
-
|
|
32
|
+
|
|
33
|
+
await util.runSeries(this.beforeValidate.map(f => f.bind(opts, data)), `${this.name}.beforeValidate`)
|
|
33
34
|
|
|
34
35
|
// Recurse and validate fields
|
|
35
36
|
let response = util.toArray(data).map(item => {
|
package/lib/util.js
CHANGED
|
@@ -309,10 +309,11 @@ module.exports = {
|
|
|
309
309
|
return variable
|
|
310
310
|
},
|
|
311
311
|
|
|
312
|
-
runSeries: function(tasks, cb) {
|
|
312
|
+
runSeries: function(tasks, info, cb) {
|
|
313
313
|
/*
|
|
314
314
|
* Runs functions in series and calls the cb when done
|
|
315
315
|
* @param {function(err, result)[]} tasks - array of functions
|
|
316
|
+
* @param {object} <info> - data to pass to the error
|
|
316
317
|
* @param {function(err, results[])} <cb>
|
|
317
318
|
* @return promise
|
|
318
319
|
* @source https://github.com/feross/run-series
|
|
@@ -322,9 +323,10 @@ module.exports = {
|
|
|
322
323
|
let isSync = true
|
|
323
324
|
|
|
324
325
|
return new Promise((res, rej) => {
|
|
325
|
-
function
|
|
326
|
-
if (i
|
|
327
|
-
|
|
326
|
+
function next(i, err, result) { // aka next(err, data)
|
|
327
|
+
if (i !== current) {
|
|
328
|
+
console.error(`Monastery ${info} error: you cannot return a promise AND call next()`)
|
|
329
|
+
return
|
|
328
330
|
}
|
|
329
331
|
results.push(result)
|
|
330
332
|
if (!err && ++current < tasks.length) callTask(current)
|
|
@@ -340,10 +342,10 @@ module.exports = {
|
|
|
340
342
|
else res(results)
|
|
341
343
|
}
|
|
342
344
|
function callTask(i) {
|
|
343
|
-
const
|
|
344
|
-
const res = tasks[i](
|
|
345
|
+
const next2 = next.bind(null, i)
|
|
346
|
+
const res = tasks[i](next2)
|
|
345
347
|
if (res instanceof Promise) {
|
|
346
|
-
res.then((result) =>
|
|
348
|
+
res.then((result) => next2(null, result)).catch(next2)
|
|
347
349
|
}
|
|
348
350
|
}
|
|
349
351
|
|
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.0.
|
|
5
|
+
"version": "3.0.6",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/test/crud.js
CHANGED
|
@@ -856,4 +856,94 @@ test('hooks > async', async () => {
|
|
|
856
856
|
// Third async hook throws an error
|
|
857
857
|
await expect(user.find({ query: user3Doc._id }))
|
|
858
858
|
.rejects.toThrow('An async error occurred with Martin3')
|
|
859
|
+
})
|
|
860
|
+
|
|
861
|
+
test('hooks > async and next conflict', async () => {
|
|
862
|
+
let user1 = db.model('user', {
|
|
863
|
+
fields: { age: { type: 'number'} },
|
|
864
|
+
afterFind: [
|
|
865
|
+
async (data, next) => {
|
|
866
|
+
const promise = await new Promise((resolve, reject) => {
|
|
867
|
+
if (data.age === 0) {
|
|
868
|
+
setTimeout(() => {
|
|
869
|
+
data.age = data.age + 1
|
|
870
|
+
resolve(data)
|
|
871
|
+
}, 100)
|
|
872
|
+
} else {
|
|
873
|
+
resolve(data)
|
|
874
|
+
}
|
|
875
|
+
})
|
|
876
|
+
next() // should console an error after waiting for the promise to finish
|
|
877
|
+
return promise
|
|
878
|
+
},
|
|
879
|
+
async (data, next) => {
|
|
880
|
+
data.age = data.age + 1
|
|
881
|
+
},
|
|
882
|
+
],
|
|
883
|
+
})
|
|
884
|
+
let user2 = db.model('user2', {
|
|
885
|
+
fields: { age: { type: 'number'} },
|
|
886
|
+
afterFind: [
|
|
887
|
+
async (data, next) => {
|
|
888
|
+
await new Promise((resolve, reject) => {
|
|
889
|
+
setTimeout(() => {
|
|
890
|
+
data.age = data.age + 1
|
|
891
|
+
resolve(data)
|
|
892
|
+
}, 100)
|
|
893
|
+
})
|
|
894
|
+
next() // should console an error after waiting for the promise to finish, without returning a promise
|
|
895
|
+
},
|
|
896
|
+
async (data, next) => {
|
|
897
|
+
data.age = data.age + 1
|
|
898
|
+
},
|
|
899
|
+
],
|
|
900
|
+
})
|
|
901
|
+
let user3 = db.model('user3', {
|
|
902
|
+
fields: { age: { type: 'number'} },
|
|
903
|
+
afterFind: [
|
|
904
|
+
async (data, next) => {
|
|
905
|
+
next() // should console an error for empty promise
|
|
906
|
+
},
|
|
907
|
+
async (data, next) => {
|
|
908
|
+
data.age = data.age + 1
|
|
909
|
+
},
|
|
910
|
+
],
|
|
911
|
+
})
|
|
912
|
+
let user4 = db.model('user3', {
|
|
913
|
+
fields: { age: { type: 'number'} },
|
|
914
|
+
afterFind: [
|
|
915
|
+
async (data, next) => {
|
|
916
|
+
return await new Promise((resolve, reject) => {
|
|
917
|
+
setTimeout(() => {
|
|
918
|
+
data.age = data.age + 1
|
|
919
|
+
next() // should console an error
|
|
920
|
+
resolve(data)
|
|
921
|
+
}, 100)
|
|
922
|
+
})
|
|
923
|
+
},
|
|
924
|
+
async (data, next) => {
|
|
925
|
+
data.age = data.age + 1
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
})
|
|
929
|
+
let user1Doc = await user1.insert({ data: { age: 0 } })
|
|
930
|
+
let user2Doc = await user2.insert({ data: { age: 0 } })
|
|
931
|
+
let user3Doc = await user3.insert({ data: { age: 0 } })
|
|
932
|
+
let user4Doc = await user4.insert({ data: { age: 0 } })
|
|
933
|
+
|
|
934
|
+
const logSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
935
|
+
|
|
936
|
+
// Only increment twice
|
|
937
|
+
await expect(user1.find({ query: user1Doc._id })).resolves.toEqual({ _id: expect.any(Object), age: 2 })
|
|
938
|
+
expect(logSpy).toHaveBeenCalledWith('Monastery afterFind error: you cannot return a promise AND call next()')
|
|
939
|
+
|
|
940
|
+
await expect(user2.find({ query: user2Doc._id })).resolves.toEqual({ _id: expect.any(Object), age: 2 })
|
|
941
|
+
expect(logSpy).toHaveBeenCalledWith('Monastery afterFind error: you cannot return a promise AND call next()')
|
|
942
|
+
|
|
943
|
+
await expect(user3.find({ query: user3Doc._id })).resolves.toEqual({ _id: expect.any(Object), age: 2 })
|
|
944
|
+
expect(logSpy).toHaveBeenCalledWith('Monastery afterFind error: you cannot return a promise AND call next()')
|
|
945
|
+
|
|
946
|
+
await expect(user4.find({ query: user4Doc._id })).resolves.toEqual({ _id: expect.any(Object), age: 2 })
|
|
947
|
+
expect(logSpy).toHaveBeenCalledWith('Monastery afterFind error: you cannot return a promise AND call next()')
|
|
948
|
+
|
|
859
949
|
})
|