monastery 1.31.7 → 1.32.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.
- package/changelog.md +23 -0
- package/docs/image-plugin.md +6 -4
- package/docs/model/find.md +1 -0
- package/lib/model-crud.js +16 -14
- package/lib/model-validate.js +3 -3
- package/lib/model.js +4 -1
- package/package.json +1 -1
- package/plugins/images/index.js +68 -18
- package/test/crud.js +54 -0
- package/test/model.js +23 -0
- package/test/plugin-images.js +53 -0
package/changelog.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
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.2](https://github.com/boycce/monastery/compare/1.32.1...1.32.2) (2022-03-04)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* missing original non-validated this.data in beforeUpdate ([4b4002d](https://github.com/boycce/monastery/commit/4b4002d3d4d2609025cbb22cacecf948252be38b))
|
|
11
|
+
|
|
12
|
+
### [1.32.1](https://github.com/boycce/monastery/compare/1.32.0...1.32.1) (2022-03-01)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* processAfterFind bug ([3183b79](https://github.com/boycce/monastery/commit/3183b79fc288665000b63e0221fbe8acf6f482aa))
|
|
18
|
+
|
|
19
|
+
## [1.32.0](https://github.com/boycce/monastery/compare/1.31.7...1.32.0) (2022-02-28)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* added getSignedUrl(s) ([3552a4d](https://github.com/boycce/monastery/commit/3552a4d0b21c192a256a590e3ac1cb48b31c6564))
|
|
25
|
+
* added image optiosn filename, and params ([353b2f0](https://github.com/boycce/monastery/commit/353b2f09ed429a5cd8d74a3b2e94493650fb52e4))
|
|
26
|
+
|
|
5
27
|
### [1.31.7](https://github.com/boycce/monastery/compare/1.31.6...1.31.7) (2022-02-28)
|
|
6
28
|
|
|
7
29
|
|
|
@@ -14,6 +36,7 @@ All notable changes to this project will be documented in this file. See [standa
|
|
|
14
36
|
|
|
15
37
|
### Bug Fixes
|
|
16
38
|
|
|
39
|
+
* Fixed validateUndefined ([58daed1](https://github.com/boycce/monastery/commit/58daed1ca5317c061a4ddde280bf45b0a134ab30))
|
|
17
40
|
* added partial unqiue index tests ([ff6f193](https://github.com/boycce/monastery/commit/ff6f1938e333407ee17895873d2b42fa5263d7e3))
|
|
18
41
|
* scripts ([bc32680](https://github.com/boycce/monastery/commit/bc326809098ae24686158a0386fbbd6671d86c98))
|
|
19
42
|
|
package/docs/image-plugin.md
CHANGED
|
@@ -26,9 +26,12 @@ Then in your model schema, e.g.
|
|
|
26
26
|
```js
|
|
27
27
|
let user = db.model('user', { fields: {
|
|
28
28
|
logo: {
|
|
29
|
-
type: 'image',
|
|
30
|
-
formats: ['bmp', 'gif', 'jpg', 'jpeg', 'png', 'tiff'],
|
|
31
|
-
|
|
29
|
+
type: 'image', // required
|
|
30
|
+
formats: ['bmp', 'gif', 'jpg', 'jpeg', 'png', 'tiff'],
|
|
31
|
+
filename: 'avatar',
|
|
32
|
+
filesize: 1000 * 1000 * 5, // max size in bytes
|
|
33
|
+
getSignedUrl: true, // get a s3 signed url after every `find()` operation (can be overridden per request)
|
|
34
|
+
params: {}, // upload params, https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property
|
|
32
35
|
},
|
|
33
36
|
logos: [{
|
|
34
37
|
type: 'image'
|
|
@@ -61,4 +64,3 @@ user.update({
|
|
|
61
64
|
Due to known limitations, we are inaccurately able to validate non-binary file types (e.g. txt, svg) before uploading to S3, and rely on their file processing to remove any malicious files.
|
|
62
65
|
|
|
63
66
|
...to be continued
|
|
64
|
-
|
package/docs/model/find.md
CHANGED
|
@@ -15,6 +15,7 @@ Find document(s) in a collection and call related hook: `schema.afterFind`
|
|
|
15
15
|
- [[`options.populate`](#populate)] *(array)*
|
|
16
16
|
- [`options.sort`] *(string\|array\|object)*: same as the mongodb option, but allows string parsing e.g. 'name', 'name:1'
|
|
17
17
|
- [`options.blacklist`] *(array\|string\|false)*: augment `schema.findBL`. `false` will remove all blacklisting
|
|
18
|
+
- [`options.getSignedUrls`] *(boolean)*: get signed urls for all image objects
|
|
18
19
|
- [`options.project`] *(string\|array\|object)*: return only these fields, ignores blacklisting
|
|
19
20
|
- [[`any mongodb option`](http://mongodb.github.io/node-mongodb-native/3.2/api/Collection.html#find)] *(any)*
|
|
20
21
|
|
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
|
|
@@ -238,6 +239,7 @@ module.exports = {
|
|
|
238
239
|
* @param {object} opts
|
|
239
240
|
* @param {object} <opts.query> - mongodb query object
|
|
240
241
|
* @param {boolean} <opts.respond> - automatically call res.json/error (requires opts.req)
|
|
242
|
+
* @param {boolean=true} <opts.multi> - set to false to limit the deletion to just one document
|
|
241
243
|
* @param {any} <opts.any> - any mongodb option
|
|
242
244
|
* @param {function} <cb> - execute cb(err, data) instead of responding
|
|
243
245
|
* @this model
|
|
@@ -304,7 +306,7 @@ module.exports = {
|
|
|
304
306
|
opts.query = util.removeUndefined(opts.query)
|
|
305
307
|
|
|
306
308
|
// Query options
|
|
307
|
-
opts.limit = opts.one? 1 : parseInt(opts.limit ||
|
|
309
|
+
opts.limit = opts.one? 1 : parseInt(opts.limit || this.manager.limit || 0)
|
|
308
310
|
opts.skip = Math.max(0, opts.skip || 0)
|
|
309
311
|
opts.sort = opts.sort || { 'createdAt': -1 }
|
|
310
312
|
if (util.isString(opts.sort)) {
|
|
@@ -324,7 +326,7 @@ module.exports = {
|
|
|
324
326
|
return opts
|
|
325
327
|
},
|
|
326
328
|
|
|
327
|
-
_processAfterFind: function(data, projection, afterFindContext) {
|
|
329
|
+
_processAfterFind: function(data, projection={}, afterFindContext={}) {
|
|
328
330
|
/**
|
|
329
331
|
* Todo: Maybe make this method public?
|
|
330
332
|
* Recurses through fields that are models and populates missing default-fields and calls model.afterFind([fn,..])
|
package/lib/model-validate.js
CHANGED
|
@@ -290,9 +290,9 @@ module.exports = {
|
|
|
290
290
|
},
|
|
291
291
|
|
|
292
292
|
_ignoredRules: [ // todo: change name? i.e. 'specialFields'
|
|
293
|
-
// Need to remove
|
|
294
|
-
'default', 'defaultOverride', '
|
|
295
|
-
'nullObject', 'timestampField', 'type', 'virtual'
|
|
293
|
+
// Need to remove filesize and formats..
|
|
294
|
+
'default', 'defaultOverride', 'filename', 'filesize', 'formats', 'image', 'index', 'insertOnly',
|
|
295
|
+
'model', 'nullObject', 'params', 'getSignedUrl', 'timestampField', 'type', 'virtual'
|
|
296
296
|
]
|
|
297
297
|
|
|
298
298
|
}
|
package/lib/model.js
CHANGED
|
@@ -188,9 +188,12 @@ Model.prototype._setupFields = function(fields) {
|
|
|
188
188
|
|
|
189
189
|
// Rule doesn't exist
|
|
190
190
|
util.forEach(field, (rule, ruleName) => {
|
|
191
|
+
if ((this.rules[ruleName] || rules[ruleName]) && this._ignoredRules.indexOf(ruleName) != -1) {
|
|
192
|
+
this.error(`The rule name "${ruleName}" for the model "${this.name}" is a reserved keyword, ignoring rule.`)
|
|
193
|
+
}
|
|
191
194
|
if (!this.rules[ruleName] && !rules[ruleName] && this._ignoredRules.indexOf(ruleName) == -1) {
|
|
192
195
|
// console.log(field)
|
|
193
|
-
this.error(`No rule "${ruleName}" exists for model "${this.name}"
|
|
196
|
+
this.error(`No rule "${ruleName}" exists for model "${this.name}", ignoring rule.`)
|
|
194
197
|
delete field[ruleName]
|
|
195
198
|
}
|
|
196
199
|
}, this)
|
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.
|
|
5
|
+
"version": "1.32.2",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": "github:boycce/monastery",
|
|
8
8
|
"homepage": "https://boycce.github.io/monastery/",
|
package/plugins/images/index.js
CHANGED
|
@@ -32,7 +32,9 @@ let plugin = module.exports = {
|
|
|
32
32
|
return
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
// Create s3 service instance
|
|
35
|
+
// Create s3 'service' instance
|
|
36
|
+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#constructor-property
|
|
37
|
+
manager.getSignedUrl = this._getSignedUrl
|
|
36
38
|
this.s3 = new S3({
|
|
37
39
|
credentials: {
|
|
38
40
|
accessKeyId: this.awsAccessKeyId,
|
|
@@ -71,6 +73,9 @@ let plugin = module.exports = {
|
|
|
71
73
|
model.afterInsert.push(function(data, n) {
|
|
72
74
|
plugin.addImages(this, data).then(() => n(null, data)).catch(e => n(e))
|
|
73
75
|
})
|
|
76
|
+
model.afterFind.push(function(data, n) {
|
|
77
|
+
plugin.getSignedUrls.call(model, this, data).then(() => n(null, data)).catch(e => n(e))
|
|
78
|
+
})
|
|
74
79
|
}
|
|
75
80
|
},
|
|
76
81
|
|
|
@@ -121,16 +126,17 @@ let plugin = module.exports = {
|
|
|
121
126
|
return Promise.all(filesArr.map(file => {
|
|
122
127
|
return new Promise((resolve, reject) => {
|
|
123
128
|
let uid = nanoid.nanoid()
|
|
129
|
+
let pathFilename = filesArr.imageField.filename ? '/' + filesArr.imageField.filename : ''
|
|
124
130
|
let image = {
|
|
125
|
-
bucket:
|
|
126
|
-
date:
|
|
131
|
+
bucket: plugin.awsBucket,
|
|
132
|
+
date: plugin.manager.useMilliseconds? Date.now() : Math.floor(Date.now() / 1000),
|
|
127
133
|
filename: file.name,
|
|
128
134
|
filesize: file.size,
|
|
129
|
-
path: `${plugin.bucketDir}/${uid}.${file.ext}`,
|
|
135
|
+
path: `${plugin.bucketDir}/${uid}${pathFilename}.${file.ext}`,
|
|
130
136
|
// sizes: ['large', 'medium', 'small'],
|
|
131
|
-
uid: uid
|
|
137
|
+
uid: uid,
|
|
132
138
|
}
|
|
133
|
-
|
|
139
|
+
plugin.manager.info(
|
|
134
140
|
`Uploading '${image.filename}' to '${image.bucket}/${image.path}'`
|
|
135
141
|
)
|
|
136
142
|
if (test) {
|
|
@@ -138,10 +144,14 @@ let plugin = module.exports = {
|
|
|
138
144
|
resolve()
|
|
139
145
|
} else {
|
|
140
146
|
plugin.s3.upload({
|
|
141
|
-
Bucket:
|
|
147
|
+
Bucket: plugin.awsBucket,
|
|
142
148
|
Key: image.path,
|
|
143
149
|
Body: file.data,
|
|
144
|
-
|
|
150
|
+
// The IAM permission "s3:PutObjectACL" must be included in the appropriate policy
|
|
151
|
+
ACL: 'public-read',
|
|
152
|
+
// upload params,https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property
|
|
153
|
+
...filesArr.imageField.params,
|
|
154
|
+
|
|
145
155
|
}, (err, response) => {
|
|
146
156
|
if (err) return reject(err)
|
|
147
157
|
plugin._addImageObjectsToData(filesArr.inputPath, data, image)
|
|
@@ -161,7 +171,7 @@ let plugin = module.exports = {
|
|
|
161
171
|
return model._update(
|
|
162
172
|
idquery,
|
|
163
173
|
{ '$set': prunedData },
|
|
164
|
-
{ 'multi': options.multi || options.create }
|
|
174
|
+
{ 'multi': options.multi || options.create },
|
|
165
175
|
)
|
|
166
176
|
|
|
167
177
|
// If errors, remove inserted documents to prevent double ups when the user resaves.
|
|
@@ -172,6 +182,30 @@ let plugin = module.exports = {
|
|
|
172
182
|
})
|
|
173
183
|
},
|
|
174
184
|
|
|
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
|
+
*/
|
|
193
|
+
// Not wanting signed urls for this operation?
|
|
194
|
+
if (util.isDefined(options.getSignedUrls) && !options.getSignedUrls) return
|
|
195
|
+
|
|
196
|
+
// Find all image objects in data
|
|
197
|
+
for (let doc of util.toArray(data)) {
|
|
198
|
+
for (let imageField of this.imageFields) {
|
|
199
|
+
if (options.getSignedUrls || imageField.getSignedUrl) {
|
|
200
|
+
let images = plugin._findImagesInData(doc, imageField, 0, '').filter(o => o.image)
|
|
201
|
+
for (let image of images) {
|
|
202
|
+
image.image.signedUrl = plugin._getSignedUrl(image.image.path)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
|
|
175
209
|
keepImagePlacement: async function(options, data) {
|
|
176
210
|
/**
|
|
177
211
|
* Hook before update/remove
|
|
@@ -303,7 +337,7 @@ let plugin = module.exports = {
|
|
|
303
337
|
{ Key: `medium/${key}.jpg` },
|
|
304
338
|
{ Key: `large/${key}.jpg` }
|
|
305
339
|
)
|
|
306
|
-
|
|
340
|
+
plugin.manager.info(
|
|
307
341
|
`Removing '${pre.image.filename}' from '${pre.image.bucket}/${pre.image.path}'`
|
|
308
342
|
)
|
|
309
343
|
}
|
|
@@ -376,7 +410,7 @@ let plugin = module.exports = {
|
|
|
376
410
|
return Promise.all(filesArr.map((file, i) => {
|
|
377
411
|
return new Promise((resolve, reject) => {
|
|
378
412
|
fileType.fromBuffer(file.data).then(res => {
|
|
379
|
-
let maxSize = filesArr.imageField.
|
|
413
|
+
let maxSize = filesArr.imageField.filesize
|
|
380
414
|
let formats = filesArr.imageField.formats || plugin.formats
|
|
381
415
|
let allowAny = util.inArray(formats, 'any')
|
|
382
416
|
file.format = res? res.ext : ''
|
|
@@ -421,17 +455,20 @@ let plugin = module.exports = {
|
|
|
421
455
|
// Subdocument field
|
|
422
456
|
if (util.isSubdocument(field)) {//schema.isObject
|
|
423
457
|
// log(`Recurse 1: ${path2}`)
|
|
424
|
-
list = list.concat(
|
|
458
|
+
list = list.concat(plugin._findAndTransformImageFields(field, path2))
|
|
425
459
|
|
|
426
460
|
// Array field
|
|
427
461
|
} else if (util.isArray(field)) {//schema.isArray
|
|
428
462
|
// log(`Recurse 2: ${path2}`)
|
|
429
|
-
list = list.concat(
|
|
463
|
+
list = list.concat(plugin._findAndTransformImageFields(field, path2))
|
|
430
464
|
|
|
431
465
|
// Image field. Test for field.image as field.type may be 'any'
|
|
432
466
|
} else if (field.type == 'image' || field.image) {
|
|
433
467
|
let formats = field.formats
|
|
434
|
-
let
|
|
468
|
+
let filesize = field.filesize || field.fileSize // old <= v1.31.7
|
|
469
|
+
let filename = field.filename
|
|
470
|
+
let getSignedUrl = field.getSignedUrl
|
|
471
|
+
let params = { ...field.params||{} }
|
|
435
472
|
// Convert image field to subdocument
|
|
436
473
|
fields[fieldName] = {
|
|
437
474
|
bucket: { type: 'string' },
|
|
@@ -440,13 +477,16 @@ let plugin = module.exports = {
|
|
|
440
477
|
filesize: { type: 'number' },
|
|
441
478
|
path: { type: 'string' },
|
|
442
479
|
schema: { image: true, nullObject: true, isImageObject: true },
|
|
443
|
-
uid: { type: 'string' }
|
|
480
|
+
uid: { type: 'string' },
|
|
444
481
|
}
|
|
445
482
|
list.push({
|
|
446
483
|
fullPath: path2,
|
|
447
484
|
fullPathRegex: new RegExp('^' + path2.replace(/\.[0-9]+/g, '.[0-9]+').replace(/\./g, '\\.') + '$'),
|
|
448
485
|
formats: formats,
|
|
449
|
-
|
|
486
|
+
filesize: filesize,
|
|
487
|
+
filename: filename,
|
|
488
|
+
getSignedUrl: getSignedUrl,
|
|
489
|
+
params: params,
|
|
450
490
|
})
|
|
451
491
|
}
|
|
452
492
|
})
|
|
@@ -474,7 +514,7 @@ let plugin = module.exports = {
|
|
|
474
514
|
if (`${dataPath}.${m}`.match(imageField.fullPathRegex)) {
|
|
475
515
|
list.push({ imageField: imageField, dataPath: `${dataPath}.${m}`, image: target[m] })
|
|
476
516
|
} else {
|
|
477
|
-
list.push(...
|
|
517
|
+
list.push(...plugin._findImagesInData(
|
|
478
518
|
target[m],
|
|
479
519
|
imageField,
|
|
480
520
|
imageFieldChunkIndex+i+1,
|
|
@@ -495,6 +535,16 @@ let plugin = module.exports = {
|
|
|
495
535
|
}
|
|
496
536
|
|
|
497
537
|
return list
|
|
498
|
-
}
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
_getSignedUrl: (path, expires=3600) => {
|
|
541
|
+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#getSignedUrl-property
|
|
542
|
+
let signedUrl = plugin.s3.getSignedUrl('getObject', {
|
|
543
|
+
Bucket: plugin.awsBucket,
|
|
544
|
+
Key: path,
|
|
545
|
+
Expires: expires
|
|
546
|
+
})
|
|
547
|
+
return signedUrl
|
|
548
|
+
},
|
|
499
549
|
|
|
500
550
|
}
|
package/test/crud.js
CHANGED
|
@@ -415,6 +415,40 @@ module.exports = function(monastery, opendb) {
|
|
|
415
415
|
db.close()
|
|
416
416
|
})
|
|
417
417
|
|
|
418
|
+
test('remove basics', async () => {
|
|
419
|
+
let db = (await opendb(null)).db
|
|
420
|
+
let user = db.model('user', {
|
|
421
|
+
fields: {
|
|
422
|
+
name: { type: 'string' },
|
|
423
|
+
},
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
// Insert multiple
|
|
427
|
+
let inserted2 = await user.insert({ data: [{ name: 'Martin' }, { name: 'Martin' }, { name: 'Martin' }]})
|
|
428
|
+
expect(inserted2).toEqual([
|
|
429
|
+
{
|
|
430
|
+
_id: expect.any(Object),
|
|
431
|
+
name: 'Martin'
|
|
432
|
+
}, {
|
|
433
|
+
_id: expect.any(Object),
|
|
434
|
+
name: 'Martin'
|
|
435
|
+
}, {
|
|
436
|
+
_id: expect.any(Object),
|
|
437
|
+
name: 'Martin'
|
|
438
|
+
}
|
|
439
|
+
])
|
|
440
|
+
|
|
441
|
+
// Remove one
|
|
442
|
+
await expect(user.remove({ query: { name: 'Martin' }, multi: false }))
|
|
443
|
+
.resolves.toMatchObject({ deletedCount: 1, result: { n: 1, ok: 1 }})
|
|
444
|
+
|
|
445
|
+
// Remove many (default)
|
|
446
|
+
await expect(user.remove({ query: { name: 'Martin' } }))
|
|
447
|
+
.resolves.toMatchObject({ deletedCount: 2, result: { n: 2, ok: 1 }})
|
|
448
|
+
|
|
449
|
+
db.close()
|
|
450
|
+
})
|
|
451
|
+
|
|
418
452
|
test('hooks', async () => {
|
|
419
453
|
let db = (await opendb(null)).db
|
|
420
454
|
let user = db.model('user', {
|
|
@@ -482,6 +516,26 @@ module.exports = function(monastery, opendb) {
|
|
|
482
516
|
last: 'Luther'
|
|
483
517
|
})
|
|
484
518
|
|
|
519
|
+
// beforeUpdate/beforeInsert should have access to the original non-validated data
|
|
520
|
+
let user2 = db.model('user2', {
|
|
521
|
+
fields: {
|
|
522
|
+
first: { type: 'string' },
|
|
523
|
+
},
|
|
524
|
+
beforeInsert: [function (data, next) {
|
|
525
|
+
if (this.data.bad === true && !data.bad) next(new Error('error1'))
|
|
526
|
+
else next()
|
|
527
|
+
}],
|
|
528
|
+
beforeUpdate: [function (data, next) {
|
|
529
|
+
if (this.data.bad === true && !data.bad) next(new Error('error2'))
|
|
530
|
+
else next()
|
|
531
|
+
}],
|
|
532
|
+
})
|
|
533
|
+
let userDoc2 = await user2._insert({ first: 'M' })
|
|
534
|
+
await expect(user2.insert({ data: { first: 'M' } })).resolves.toMatchObject({ first: 'M' })
|
|
535
|
+
await expect(user2.insert({ data: { first: 'M', bad: true } })).rejects.toThrow('error1')
|
|
536
|
+
await expect(user2.update({ query: userDoc2._id, data: { first: 'M', } })).resolves.toEqual({ first: 'M' })
|
|
537
|
+
await expect(user2.update({ query: userDoc2._id, data: { first: 'M', bad: true } })).rejects.toThrow('error2')
|
|
538
|
+
|
|
485
539
|
db.close()
|
|
486
540
|
})
|
|
487
541
|
|
package/test/model.js
CHANGED
|
@@ -115,6 +115,29 @@ module.exports = function(monastery, opendb) {
|
|
|
115
115
|
})
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
+
test('model reserved rules', async () => {
|
|
119
|
+
// Setup
|
|
120
|
+
let db = (await opendb(false, {})).db
|
|
121
|
+
db.error = () => {} // hiding debug error
|
|
122
|
+
let user = db.model('user', {
|
|
123
|
+
fields: {
|
|
124
|
+
name: {
|
|
125
|
+
type: 'string',
|
|
126
|
+
params: {}, // reserved keyword (image plugin)
|
|
127
|
+
paramsUnreserved: {}
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
rules: {
|
|
131
|
+
params: (value) => {
|
|
132
|
+
return false // shouldn'r run
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
await expect(user.validate({ name: 'Martin' })).resolves.toMatchObject({
|
|
137
|
+
name: 'Martin',
|
|
138
|
+
})
|
|
139
|
+
})
|
|
140
|
+
|
|
118
141
|
test('model indexes', async () => {
|
|
119
142
|
// Setup: Need to test different types of indexes
|
|
120
143
|
let db = (await opendb(null)).db
|
package/test/plugin-images.js
CHANGED
|
@@ -707,4 +707,57 @@ module.exports = function(monastery, opendb) {
|
|
|
707
707
|
db.close()
|
|
708
708
|
})
|
|
709
709
|
|
|
710
|
+
test('images getSignedUrls', async () => {
|
|
711
|
+
// latest (2022.02)
|
|
712
|
+
let db = (await opendb(null, {
|
|
713
|
+
timestamps: false,
|
|
714
|
+
serverSelectionTimeoutMS: 2000,
|
|
715
|
+
imagePlugin: { awsBucket: 'fake', awsAccessKeyId: 'fake', awsSecretAccessKey: 'fake' }
|
|
716
|
+
})).db
|
|
717
|
+
|
|
718
|
+
db.model('user', { fields: {
|
|
719
|
+
photos: [{ type: 'image' }],
|
|
720
|
+
photos2: [{ type: 'image', getSignedUrl: true }],
|
|
721
|
+
}})
|
|
722
|
+
|
|
723
|
+
let image = {
|
|
724
|
+
bucket: 'test',
|
|
725
|
+
date: 1234,
|
|
726
|
+
filename: 'lion1.png',
|
|
727
|
+
filesize: 1234,
|
|
728
|
+
path: 'test/lion1.png',
|
|
729
|
+
uid: 'lion1'
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
let userInserted = await db.user._insert({
|
|
733
|
+
photos: [image, image],
|
|
734
|
+
photos2: [image, image],
|
|
735
|
+
})
|
|
736
|
+
|
|
737
|
+
// Find signed URL via query option
|
|
738
|
+
let imageWithSignedUrl = { ...image, signedUrl: expect.stringMatching(/^https/) }
|
|
739
|
+
await expect(db.user.findOne({ query: userInserted._id, getSignedUrls: true })).resolves.toEqual({
|
|
740
|
+
_id: expect.any(Object),
|
|
741
|
+
photos: [imageWithSignedUrl, imageWithSignedUrl],
|
|
742
|
+
photos2: [imageWithSignedUrl, imageWithSignedUrl],
|
|
743
|
+
})
|
|
744
|
+
|
|
745
|
+
// Find signed URL via schema option
|
|
746
|
+
await expect(db.user.findOne({ query: userInserted._id })).resolves.toEqual({
|
|
747
|
+
_id: expect.any(Object),
|
|
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],
|
|
758
|
+
})
|
|
759
|
+
|
|
760
|
+
db.close()
|
|
761
|
+
})
|
|
762
|
+
|
|
710
763
|
}
|