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 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
 
@@ -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 by default after `find()`
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
- opts.data = await this.validate(opts.data||{}, { ...opts })
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, opts.data)))
35
- let response = await this._insert(opts.data, options)
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(opts.data)) opts.data = await this.validate(opts.data, { ...opts })
202
- if (!util.isDefined(opts.data) && util.isEmpty(operators)) {
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(opts.data) && (!opts.data || util.isEmpty(opts.data))) {
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, opts.data||{})))
210
- if (opts.data && operators['$set']) {
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 (opts.data || operators['$set']) {
214
- operators['$set'] = { ...opts.data, ...(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,..])
@@ -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.0",
5
+ "version": "1.32.3",
6
6
  "license": "MIT",
7
7
  "repository": "github:boycce/monastery",
8
8
  "homepage": "https://boycce.github.io/monastery/",
@@ -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: this.awsBucket,
132
- date: this.manager.useMilliseconds? Date.now() : Math.floor(Date.now() / 1000),
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
- this.manager.info(
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: this.awsBucket,
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 options.model.imageFields) {
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 = this._getSignedUrl(image.image.path)
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
- this.manager.info(
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(this._findAndTransformImageFields(field, path2))
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(this._findAndTransformImageFields(field, path2))
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(...this._findImagesInData(
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
- // Missing parameters
57
- await expect(user.find()).rejects.toThrow('Please pass an object or MongoId to options.query')
58
- await expect(user.find(undefined)).rejects.toThrow('Please pass an object or MongoId to options.query')
59
- await expect(user.find({})).rejects.toThrow('Please pass an object or MongoId to options.query')
60
- await expect(user.find({ query: null })).rejects.toThrow('Please pass an object or MongoId to options.query')
61
- await expect(user.find({ query: undefined })).rejects.toThrow('Please pass an object or MongoId to options.query')
62
- await expect(user.find({ query: { _id: undefined }}))
63
- .rejects.toThrow('Please pass an object or MongoId to options.query')
64
- await expect(user.find(1)).rejects.toThrow('Please pass an object or MongoId to options.query')
65
-
66
- // Bad MongoID
67
- await expect(user.find({ query: '' })).resolves.toEqual(null)
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
 
@@ -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: { fileSize: 1 * 1000 * 1000, files: 10 }}))
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: { fileSize: 1 * 1000 * 1000, files: 10 }}))
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: { fileSize: 1 * 1000 * 1000, files: 10 }}))
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', fileSize: 1000 * 100 },
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: { fileSize: 1000 * 200, files: 10 }}))
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
- { ...image, signedUrl: expect.stringMatching(/^https/) },
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
- // Find signed URL
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
- { ...image },
754
- { ...image },
755
- ],
756
- photos2: [
757
- { ...image, signedUrl: expect.stringMatching(/^https/) },
758
- { ...image, signedUrl: expect.stringMatching(/^https/) },
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()