monastery 1.32.0 → 1.32.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 +22 -0
- package/docs/image-plugin.md +1 -1
- package/lib/model-crud.js +14 -13
- package/lib/model-validate.js +1 -1
- package/package.json +1 -1
- package/plugins/images/index.js +18 -11
- package/test/crud.js +37 -13
- package/test/plugin-images.js +21 -23
package/changelog.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
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.32.3](https://github.com/boycce/monastery/compare/1.32.2...1.32.3) (2022-03-07)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* fileSize warning ([3824b23](https://github.com/boycce/monastery/commit/3824b23883fad508e081d2a2bce1c340ec2775dc))
|
|
11
|
+
|
|
12
|
+
### [1.32.2](https://github.com/boycce/monastery/compare/1.32.1...1.32.2) (2022-03-04)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* missing original non-validated this.data in beforeUpdate ([4b4002d](https://github.com/boycce/monastery/commit/4b4002d3d4d2609025cbb22cacecf948252be38b))
|
|
18
|
+
|
|
19
|
+
### [1.32.1](https://github.com/boycce/monastery/compare/1.32.0...1.32.1) (2022-03-01)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* processAfterFind bug ([3183b79](https://github.com/boycce/monastery/commit/3183b79fc288665000b63e0221fbe8acf6f482aa))
|
|
25
|
+
|
|
5
26
|
## [1.32.0](https://github.com/boycce/monastery/compare/1.31.7...1.32.0) (2022-02-28)
|
|
6
27
|
|
|
7
28
|
|
|
@@ -22,6 +43,7 @@ All notable changes to this project will be documented in this file. See [standa
|
|
|
22
43
|
|
|
23
44
|
### Bug Fixes
|
|
24
45
|
|
|
46
|
+
* Fixed validateUndefined ([58daed1](https://github.com/boycce/monastery/commit/58daed1ca5317c061a4ddde280bf45b0a134ab30))
|
|
25
47
|
* added partial unqiue index tests ([ff6f193](https://github.com/boycce/monastery/commit/ff6f1938e333407ee17895873d2b42fa5263d7e3))
|
|
26
48
|
* scripts ([bc32680](https://github.com/boycce/monastery/commit/bc326809098ae24686158a0386fbbd6671d86c98))
|
|
27
49
|
|
package/docs/image-plugin.md
CHANGED
|
@@ -30,7 +30,7 @@ let user = db.model('user', { fields: {
|
|
|
30
30
|
formats: ['bmp', 'gif', 'jpg', 'jpeg', 'png', 'tiff'],
|
|
31
31
|
filename: 'avatar',
|
|
32
32
|
filesize: 1000 * 1000 * 5, // max size in bytes
|
|
33
|
-
getSignedUrl: true, // get a s3 signed url
|
|
33
|
+
getSignedUrl: true, // get a s3 signed url after every `find()` operation (can be overridden per request)
|
|
34
34
|
params: {}, // upload params, https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property
|
|
35
35
|
},
|
|
36
36
|
logos: [{
|
package/lib/model-crud.js
CHANGED
|
@@ -28,11 +28,11 @@ module.exports = {
|
|
|
28
28
|
])
|
|
29
29
|
|
|
30
30
|
// Validate
|
|
31
|
-
|
|
31
|
+
let data = await this.validate(opts.data||{}, { ...opts })
|
|
32
32
|
|
|
33
33
|
// Insert
|
|
34
|
-
await util.runSeries(this.beforeInsert.map(f => f.bind(opts,
|
|
35
|
-
let response = await this._insert(
|
|
34
|
+
await util.runSeries(this.beforeInsert.map(f => f.bind(opts, data)))
|
|
35
|
+
let response = await this._insert(data, options)
|
|
36
36
|
await util.runSeries(this.afterInsert.map(f => f.bind(opts, response)))
|
|
37
37
|
|
|
38
38
|
// Success/error
|
|
@@ -193,31 +193,32 @@ module.exports = {
|
|
|
193
193
|
}
|
|
194
194
|
try {
|
|
195
195
|
opts = await this._queryObject(opts, 'update')
|
|
196
|
+
let data = opts.data
|
|
196
197
|
let response = null
|
|
197
198
|
let operators = util.pluck(opts, [/^\$/])
|
|
198
199
|
let options = util.omit(opts, ['data', 'query', 'respond', 'validateUndefined', 'skipValidation', 'blacklist'])
|
|
199
200
|
|
|
200
201
|
// Validate
|
|
201
|
-
if (util.isDefined(
|
|
202
|
-
if (!util.isDefined(
|
|
202
|
+
if (util.isDefined(data)) data = await this.validate(opts.data, { ...opts })
|
|
203
|
+
if (!util.isDefined(data) && util.isEmpty(operators)) {
|
|
203
204
|
throw new Error(`Please pass an update operator to ${this.name}.update(), e.g. data, $unset, etc`)
|
|
204
205
|
}
|
|
205
|
-
if (util.isDefined(
|
|
206
|
+
if (util.isDefined(data) && (!data || util.isEmpty(data))) {
|
|
206
207
|
throw new Error(`No valid data passed to ${this.name}.update({ data: .. })`)
|
|
207
208
|
}
|
|
208
|
-
// Hook: beforeUpdate
|
|
209
|
-
await util.runSeries(this.beforeUpdate.map(f => f.bind(opts,
|
|
210
|
-
if (
|
|
209
|
+
// Hook: beforeUpdate (has access to original, non-validated opts.data)
|
|
210
|
+
await util.runSeries(this.beforeUpdate.map(f => f.bind(opts, data||{})))
|
|
211
|
+
if (data && operators['$set']) {
|
|
211
212
|
this.warn(`'$set' fields take precedence over the data fields for \`${this.name}.update()\``)
|
|
212
213
|
}
|
|
213
|
-
if (
|
|
214
|
-
operators['$set'] = { ...
|
|
214
|
+
if (data || operators['$set']) {
|
|
215
|
+
operators['$set'] = { ...data, ...(operators['$set'] || {}) }
|
|
215
216
|
}
|
|
216
217
|
// Update
|
|
217
218
|
let update = await this._update(opts.query, operators, options)
|
|
218
219
|
if (update.n) response = Object.assign(Object.create({ _output: update }), operators['$set']||{})
|
|
219
220
|
|
|
220
|
-
// Hook: afterUpdate
|
|
221
|
+
// Hook: afterUpdate (doesn't have access to validated data)
|
|
221
222
|
if (update.n) await util.runSeries(this.afterUpdate.map(f => f.bind(opts, response)))
|
|
222
223
|
|
|
223
224
|
// Success
|
|
@@ -325,7 +326,7 @@ module.exports = {
|
|
|
325
326
|
return opts
|
|
326
327
|
},
|
|
327
328
|
|
|
328
|
-
_processAfterFind: function(data, projection, afterFindContext) {
|
|
329
|
+
_processAfterFind: function(data, projection={}, afterFindContext={}) {
|
|
329
330
|
/**
|
|
330
331
|
* Todo: Maybe make this method public?
|
|
331
332
|
* Recurses through fields that are models and populates missing default-fields and calls model.afterFind([fn,..])
|
package/lib/model-validate.js
CHANGED
|
@@ -291,7 +291,7 @@ module.exports = {
|
|
|
291
291
|
|
|
292
292
|
_ignoredRules: [ // todo: change name? i.e. 'specialFields'
|
|
293
293
|
// Need to remove filesize and formats..
|
|
294
|
-
'default', 'defaultOverride', 'filename', 'filesize', 'formats', 'image', 'index', 'insertOnly',
|
|
294
|
+
'default', 'defaultOverride', 'filename', 'filesize', 'fileSize', 'formats', 'image', 'index', 'insertOnly',
|
|
295
295
|
'model', 'nullObject', 'params', 'getSignedUrl', 'timestampField', 'type', 'virtual'
|
|
296
296
|
]
|
|
297
297
|
|
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
|
+
"version": "1.32.3",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/plugins/images/index.js
CHANGED
|
@@ -74,7 +74,7 @@ let plugin = module.exports = {
|
|
|
74
74
|
plugin.addImages(this, data).then(() => n(null, data)).catch(e => n(e))
|
|
75
75
|
})
|
|
76
76
|
model.afterFind.push(function(data, n) {
|
|
77
|
-
plugin.getSignedUrls(this, data).then(() => n(null, data)).catch(e => n(e))
|
|
77
|
+
plugin.getSignedUrls.call(model, this, data).then(() => n(null, data)).catch(e => n(e))
|
|
78
78
|
})
|
|
79
79
|
}
|
|
80
80
|
},
|
|
@@ -128,15 +128,15 @@ let plugin = module.exports = {
|
|
|
128
128
|
let uid = nanoid.nanoid()
|
|
129
129
|
let pathFilename = filesArr.imageField.filename ? '/' + filesArr.imageField.filename : ''
|
|
130
130
|
let image = {
|
|
131
|
-
bucket:
|
|
132
|
-
date:
|
|
131
|
+
bucket: plugin.awsBucket,
|
|
132
|
+
date: plugin.manager.useMilliseconds? Date.now() : Math.floor(Date.now() / 1000),
|
|
133
133
|
filename: file.name,
|
|
134
134
|
filesize: file.size,
|
|
135
135
|
path: `${plugin.bucketDir}/${uid}${pathFilename}.${file.ext}`,
|
|
136
136
|
// sizes: ['large', 'medium', 'small'],
|
|
137
137
|
uid: uid,
|
|
138
138
|
}
|
|
139
|
-
|
|
139
|
+
plugin.manager.info(
|
|
140
140
|
`Uploading '${image.filename}' to '${image.bucket}/${image.path}'`
|
|
141
141
|
)
|
|
142
142
|
if (test) {
|
|
@@ -144,7 +144,7 @@ let plugin = module.exports = {
|
|
|
144
144
|
resolve()
|
|
145
145
|
} else {
|
|
146
146
|
plugin.s3.upload({
|
|
147
|
-
Bucket:
|
|
147
|
+
Bucket: plugin.awsBucket,
|
|
148
148
|
Key: image.path,
|
|
149
149
|
Body: file.data,
|
|
150
150
|
// The IAM permission "s3:PutObjectACL" must be included in the appropriate policy
|
|
@@ -183,16 +183,23 @@ let plugin = module.exports = {
|
|
|
183
183
|
},
|
|
184
184
|
|
|
185
185
|
getSignedUrls: async function(options, data) {
|
|
186
|
+
/**
|
|
187
|
+
* Get signed urls for all image objects in data
|
|
188
|
+
* @param {object} options - monastery operation options {model, query, files, ..}
|
|
189
|
+
* @param {object} data
|
|
190
|
+
* @return promise(data)
|
|
191
|
+
* @this model
|
|
192
|
+
*/
|
|
186
193
|
// Not wanting signed urls for this operation?
|
|
187
194
|
if (util.isDefined(options.getSignedUrls) && !options.getSignedUrls) return
|
|
188
195
|
|
|
189
196
|
// Find all image objects in data
|
|
190
197
|
for (let doc of util.toArray(data)) {
|
|
191
|
-
for (let imageField of
|
|
198
|
+
for (let imageField of this.imageFields) {
|
|
192
199
|
if (options.getSignedUrls || imageField.getSignedUrl) {
|
|
193
200
|
let images = plugin._findImagesInData(doc, imageField, 0, '').filter(o => o.image)
|
|
194
201
|
for (let image of images) {
|
|
195
|
-
image.image.signedUrl =
|
|
202
|
+
image.image.signedUrl = plugin._getSignedUrl(image.image.path)
|
|
196
203
|
}
|
|
197
204
|
}
|
|
198
205
|
}
|
|
@@ -330,7 +337,7 @@ let plugin = module.exports = {
|
|
|
330
337
|
{ Key: `medium/${key}.jpg` },
|
|
331
338
|
{ Key: `large/${key}.jpg` }
|
|
332
339
|
)
|
|
333
|
-
|
|
340
|
+
plugin.manager.info(
|
|
334
341
|
`Removing '${pre.image.filename}' from '${pre.image.bucket}/${pre.image.path}'`
|
|
335
342
|
)
|
|
336
343
|
}
|
|
@@ -448,12 +455,12 @@ let plugin = module.exports = {
|
|
|
448
455
|
// Subdocument field
|
|
449
456
|
if (util.isSubdocument(field)) {//schema.isObject
|
|
450
457
|
// log(`Recurse 1: ${path2}`)
|
|
451
|
-
list = list.concat(
|
|
458
|
+
list = list.concat(plugin._findAndTransformImageFields(field, path2))
|
|
452
459
|
|
|
453
460
|
// Array field
|
|
454
461
|
} else if (util.isArray(field)) {//schema.isArray
|
|
455
462
|
// log(`Recurse 2: ${path2}`)
|
|
456
|
-
list = list.concat(
|
|
463
|
+
list = list.concat(plugin._findAndTransformImageFields(field, path2))
|
|
457
464
|
|
|
458
465
|
// Image field. Test for field.image as field.type may be 'any'
|
|
459
466
|
} else if (field.type == 'image' || field.image) {
|
|
@@ -507,7 +514,7 @@ let plugin = module.exports = {
|
|
|
507
514
|
if (`${dataPath}.${m}`.match(imageField.fullPathRegex)) {
|
|
508
515
|
list.push({ imageField: imageField, dataPath: `${dataPath}.${m}`, image: target[m] })
|
|
509
516
|
} else {
|
|
510
|
-
list.push(...
|
|
517
|
+
list.push(...plugin._findImagesInData(
|
|
511
518
|
target[m],
|
|
512
519
|
imageField,
|
|
513
520
|
imageFieldChunkIndex+i+1,
|
package/test/crud.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module.exports = function(monastery, opendb) {
|
|
4
4
|
|
|
5
5
|
test('basic operator calls', async () => {
|
|
6
|
+
// Todo: take out and group query/id parsing tests
|
|
6
7
|
let db = (await opendb(null)).db
|
|
7
8
|
let user = db.model('user', {
|
|
8
9
|
fields: {
|
|
@@ -53,20 +54,23 @@ module.exports = function(monastery, opendb) {
|
|
|
53
54
|
let find6 = await user.find(inserted2[0]._id.toString())
|
|
54
55
|
expect(find6).toEqual({ _id: inserted2[0]._id, name: 'Martin Luther1' })
|
|
55
56
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
await expect(user.find(
|
|
59
|
-
await expect(user.find(
|
|
60
|
-
await expect(user.find(
|
|
61
|
-
await expect(user.find(
|
|
62
|
-
await expect(user.find({
|
|
63
|
-
|
|
64
|
-
await expect(user.find(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
await expect(user.find('bad-id')).resolves.toEqual(null)
|
|
57
|
+
// Bad ids
|
|
58
|
+
let badIdOrQuery = 'Please pass an object or MongoId to options.query'
|
|
59
|
+
await expect(user.find()).rejects.toThrow(badIdOrQuery)
|
|
60
|
+
await expect(user.find(1)).rejects.toThrow(badIdOrQuery)
|
|
61
|
+
await expect(user.find(null)).rejects.toThrow(badIdOrQuery)
|
|
62
|
+
await expect(user.find(undefined)).rejects.toThrow(badIdOrQuery)
|
|
63
|
+
await expect(user.find({})).rejects.toThrow(badIdOrQuery)
|
|
64
|
+
await expect(user.find({ query: null })).rejects.toThrow(badIdOrQuery)
|
|
65
|
+
await expect(user.find({ query: undefined })).rejects.toThrow(badIdOrQuery)
|
|
66
|
+
await expect(user.find({ query: { _id: undefined }})).rejects.toThrow(badIdOrQuery)
|
|
67
|
+
|
|
68
|
+
// Parseable
|
|
69
69
|
await expect(user.find('')).resolves.toEqual(null)
|
|
70
|
+
await expect(user.find('invalid-id')).resolves.toEqual(null)
|
|
71
|
+
await expect(user.find({ query: '' })).resolves.toEqual(null)
|
|
72
|
+
await expect(user.find({ query: { _id: '' }})).resolves.toEqual(null)
|
|
73
|
+
await expect(user.find({ query: { _id: null }})).resolves.toEqual([]) // should throw error
|
|
70
74
|
|
|
71
75
|
// FindOne (query id)
|
|
72
76
|
let findOne = await user.findOne({ query: inserted._id })
|
|
@@ -516,6 +520,26 @@ module.exports = function(monastery, opendb) {
|
|
|
516
520
|
last: 'Luther'
|
|
517
521
|
})
|
|
518
522
|
|
|
523
|
+
// beforeUpdate/beforeInsert should have access to the original non-validated data
|
|
524
|
+
let user2 = db.model('user2', {
|
|
525
|
+
fields: {
|
|
526
|
+
first: { type: 'string' },
|
|
527
|
+
},
|
|
528
|
+
beforeInsert: [function (data, next) {
|
|
529
|
+
if (this.data.bad === true && !data.bad) next(new Error('error1'))
|
|
530
|
+
else next()
|
|
531
|
+
}],
|
|
532
|
+
beforeUpdate: [function (data, next) {
|
|
533
|
+
if (this.data.bad === true && !data.bad) next(new Error('error2'))
|
|
534
|
+
else next()
|
|
535
|
+
}],
|
|
536
|
+
})
|
|
537
|
+
let userDoc2 = await user2._insert({ first: 'M' })
|
|
538
|
+
await expect(user2.insert({ data: { first: 'M' } })).resolves.toMatchObject({ first: 'M' })
|
|
539
|
+
await expect(user2.insert({ data: { first: 'M', bad: true } })).rejects.toThrow('error1')
|
|
540
|
+
await expect(user2.update({ query: userDoc2._id, data: { first: 'M', } })).resolves.toEqual({ first: 'M' })
|
|
541
|
+
await expect(user2.update({ query: userDoc2._id, data: { first: 'M', bad: true } })).rejects.toThrow('error2')
|
|
542
|
+
|
|
519
543
|
db.close()
|
|
520
544
|
})
|
|
521
545
|
|
package/test/plugin-images.js
CHANGED
|
@@ -161,7 +161,7 @@ module.exports = function(monastery, opendb) {
|
|
|
161
161
|
let express = require('express')
|
|
162
162
|
let upload = require('express-fileupload')
|
|
163
163
|
let app = express()
|
|
164
|
-
app.use(upload({ limits: {
|
|
164
|
+
app.use(upload({ limits: { filesize: 1 * 1000 * 1000, files: 10 }}))
|
|
165
165
|
|
|
166
166
|
app.post('/', function(req, res) {
|
|
167
167
|
// Files exist
|
|
@@ -292,7 +292,7 @@ module.exports = function(monastery, opendb) {
|
|
|
292
292
|
let express = require('express')
|
|
293
293
|
let upload = require('express-fileupload')
|
|
294
294
|
let app = express()
|
|
295
|
-
app.use(upload({ limits: {
|
|
295
|
+
app.use(upload({ limits: { filesize: 1 * 1000 * 1000, files: 10 }}))
|
|
296
296
|
|
|
297
297
|
app.post('/', function(req, res) {
|
|
298
298
|
req.body.logos = JSON.parse(req.body.logos)
|
|
@@ -368,7 +368,7 @@ module.exports = function(monastery, opendb) {
|
|
|
368
368
|
let express = require('express')
|
|
369
369
|
let upload = require('express-fileupload')
|
|
370
370
|
let app = express()
|
|
371
|
-
app.use(upload({ limits: {
|
|
371
|
+
app.use(upload({ limits: { filesize: 1 * 1000 * 1000, files: 10 }}))
|
|
372
372
|
|
|
373
373
|
app.post('/', function(req, res) {
|
|
374
374
|
try {
|
|
@@ -417,7 +417,7 @@ module.exports = function(monastery, opendb) {
|
|
|
417
417
|
imageSvgBad: { type: 'image' },
|
|
418
418
|
imageSvgGood: { type: 'image', formats: ['svg'] },
|
|
419
419
|
imageSvgAny: { type: 'image', formats: ['any'] },
|
|
420
|
-
imageSize1: { type: 'image',
|
|
420
|
+
imageSize1: { type: 'image', filesize: 1000 * 100 },
|
|
421
421
|
imageSize2: { type: 'image' },
|
|
422
422
|
}})
|
|
423
423
|
|
|
@@ -426,7 +426,7 @@ module.exports = function(monastery, opendb) {
|
|
|
426
426
|
let express = require('express')
|
|
427
427
|
let upload = require('express-fileupload')
|
|
428
428
|
let app = express()
|
|
429
|
-
app.use(upload({ limits: {
|
|
429
|
+
app.use(upload({ limits: { filesize: 1000 * 200, files: 10 }}))
|
|
430
430
|
|
|
431
431
|
app.post('/', async (req, res) => {
|
|
432
432
|
let imageSvgBad = { imageSvgBad: req.files.imageSvgBad }
|
|
@@ -734,29 +734,27 @@ module.exports = function(monastery, opendb) {
|
|
|
734
734
|
photos2: [image, image],
|
|
735
735
|
})
|
|
736
736
|
|
|
737
|
-
// Find signed URL
|
|
737
|
+
// Find signed URL via query option
|
|
738
|
+
let imageWithSignedUrl = { ...image, signedUrl: expect.stringMatching(/^https/) }
|
|
738
739
|
await expect(db.user.findOne({ query: userInserted._id, getSignedUrls: true })).resolves.toEqual({
|
|
739
740
|
_id: expect.any(Object),
|
|
740
|
-
photos: [
|
|
741
|
-
|
|
742
|
-
{ ...image, signedUrl: expect.stringMatching(/^https/) },
|
|
743
|
-
],
|
|
744
|
-
photos2: [
|
|
745
|
-
{ ...image, signedUrl: expect.stringMatching(/^https/) },
|
|
746
|
-
{ ...image, signedUrl: expect.stringMatching(/^https/) },
|
|
747
|
-
]
|
|
741
|
+
photos: [imageWithSignedUrl, imageWithSignedUrl],
|
|
742
|
+
photos2: [imageWithSignedUrl, imageWithSignedUrl],
|
|
748
743
|
})
|
|
749
|
-
|
|
744
|
+
|
|
745
|
+
// Find signed URL via schema option
|
|
750
746
|
await expect(db.user.findOne({ query: userInserted._id })).resolves.toEqual({
|
|
751
747
|
_id: expect.any(Object),
|
|
752
|
-
photos: [
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
748
|
+
photos: [image, image],
|
|
749
|
+
photos2: [imageWithSignedUrl, imageWithSignedUrl],
|
|
750
|
+
})
|
|
751
|
+
|
|
752
|
+
// Works with _processAfterFind
|
|
753
|
+
let rawUser = await db.user._findOne({ _id: userInserted._id })
|
|
754
|
+
await expect(db.user._processAfterFind(rawUser)).resolves.toEqual({
|
|
755
|
+
_id: expect.any(Object),
|
|
756
|
+
photos: [image, image],
|
|
757
|
+
photos2: [imageWithSignedUrl, imageWithSignedUrl],
|
|
760
758
|
})
|
|
761
759
|
|
|
762
760
|
db.close()
|