@bedrockio/model 0.1.33 → 0.2.0

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.
@@ -6,9 +6,12 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.applySoftDelete = applySoftDelete;
7
7
  exports.assertUnique = assertUnique;
8
8
  exports.hasUniqueConstraints = hasUniqueConstraints;
9
+ var _lodash = require("lodash");
10
+ var _query = require("./query");
9
11
  function applySoftDelete(schema) {
10
12
  applyQueries(schema);
11
13
  applyUniqueConstraints(schema);
14
+ applyHookPatch(schema);
12
15
  }
13
16
 
14
17
  // Soft Delete Querying
@@ -48,124 +51,142 @@ function applyQueries(schema) {
48
51
 
49
52
  // Static Methods
50
53
 
51
- schema.static('deleteOne', async function deleteOne(filter, ...rest) {
54
+ schema.static('deleteOne', function deleteOne(filter, ...rest) {
52
55
  const update = getDelete();
53
- const res = await this.updateOne(filter, update, ...rest);
54
- return {
55
- acknowledged: res.acknowledged,
56
- deletedCount: res.modifiedCount
57
- };
56
+ const query = this.updateOne(filter, update, ...omitCallback(rest));
57
+ return (0, _query.wrapQuery)(query, async promise => {
58
+ const res = await promise;
59
+ return {
60
+ acknowledged: res.acknowledged,
61
+ deletedCount: res.modifiedCount
62
+ };
63
+ });
58
64
  });
59
- schema.static('deleteMany', async function deleteMany(filter, ...rest) {
65
+ schema.static('deleteMany', function deleteMany(filter, ...rest) {
60
66
  const update = getDelete();
61
- const res = await this.updateMany(filter, update, ...rest);
62
- return {
63
- acknowledged: res.acknowledged,
64
- deletedCount: res.modifiedCount
65
- };
67
+ const query = this.updateMany(filter, update, ...omitCallback(rest));
68
+ return (0, _query.wrapQuery)(query, async promise => {
69
+ const res = await promise;
70
+ return {
71
+ acknowledged: res.acknowledged,
72
+ deletedCount: res.modifiedCount
73
+ };
74
+ });
66
75
  });
67
- schema.static('findOneAndDelete', async function findOneAndDelete(filter, ...args) {
68
- return await this.findOneAndUpdate(filter, getDelete(), ...args);
76
+ schema.static('findOneAndDelete', function findOneAndDelete(filter, ...rest) {
77
+ return this.findOneAndUpdate(filter, getDelete(), ...omitCallback(rest));
69
78
  });
70
- schema.static('restoreOne', async function restoreOne(filter, ...rest) {
71
- const update = getRestore();
72
- const res = await this.updateOne(filter, update, ...rest);
73
- return {
74
- acknowledged: res.acknowledged,
75
- restoredCount: res.modifiedCount
76
- };
79
+ schema.static('restoreOne', function restoreOne(filter, ...rest) {
80
+ const query = this.updateOne(filter, getRestore(), ...omitCallback(rest));
81
+ return (0, _query.wrapQuery)(query, async promise => {
82
+ const res = await promise;
83
+ return {
84
+ acknowledged: res.acknowledged,
85
+ restoredCount: res.modifiedCount
86
+ };
87
+ });
77
88
  });
78
- schema.static('restoreMany', async function restoreMany(filter, ...rest) {
79
- const update = getRestore();
80
- const res = await this.updateMany(filter, update, ...rest);
81
- return {
82
- acknowledged: res.acknowledged,
83
- restoredCount: res.modifiedCount
84
- };
89
+ schema.static('restoreMany', function restoreMany(filter, ...rest) {
90
+ const query = this.updateMany(filter, getRestore(), ...omitCallback(rest));
91
+ return (0, _query.wrapQuery)(query, async promise => {
92
+ const res = await promise;
93
+ return {
94
+ acknowledged: res.acknowledged,
95
+ restoredCount: res.modifiedCount
96
+ };
97
+ });
85
98
  });
86
- schema.static('destroyOne', async function destroyOne(...args) {
87
- const res = await this.collection.deleteOne(...args);
88
- return {
89
- acknowledged: res.acknowledged,
90
- destroyedCount: res.deletedCount
91
- };
99
+ schema.static('destroyOne', function destroyOne(conditions, ...rest) {
100
+ // Following Mongoose patterns here
101
+ const query = new this.Query({}, {}, this, this.collection).deleteOne(conditions, ...omitCallback(rest));
102
+ return (0, _query.wrapQuery)(query, async promise => {
103
+ const res = await promise;
104
+ return {
105
+ acknowledged: res.acknowledged,
106
+ destroyedCount: res.deletedCount
107
+ };
108
+ });
92
109
  });
93
- schema.static('destroyMany', async function destroyMany(...args) {
94
- const res = await this.collection.deleteMany(...args);
95
- return {
96
- acknowledged: res.acknowledged,
97
- destroyedCount: res.deletedCount
98
- };
110
+ schema.static('destroyMany', function destroyMany(conditions, ...rest) {
111
+ // Following Mongoose patterns here
112
+ const query = new this.Query({}, {}, this, this.collection).deleteMany(conditions, ...omitCallback(rest));
113
+ return (0, _query.wrapQuery)(query, async promise => {
114
+ const res = await promise;
115
+ return {
116
+ acknowledged: res.acknowledged,
117
+ destroyedCount: res.deletedCount
118
+ };
119
+ });
99
120
  });
100
121
  schema.static('findDeleted', function findDeleted(filter, ...rest) {
101
122
  filter = {
102
123
  ...filter,
103
124
  deleted: true
104
125
  };
105
- return this.find(filter, ...rest);
126
+ return this.find(filter, ...omitCallback(rest));
106
127
  });
107
128
  schema.static('findOneDeleted', function findOneDeleted(filter, ...rest) {
108
129
  filter = {
109
130
  ...filter,
110
131
  deleted: true
111
132
  };
112
- return this.findOne(filter, ...rest);
133
+ return this.findOne(filter, ...omitCallback(rest));
113
134
  });
114
135
  schema.static('findByIdDeleted', function findByIdDeleted(id, ...rest) {
115
136
  const filter = {
116
137
  _id: id,
117
138
  deleted: true
118
139
  };
119
- return this.findOne(filter, ...rest);
140
+ return this.findOne(filter, ...omitCallback(rest));
120
141
  });
121
142
  schema.static('existsDeleted', function existsDeleted(filter, ...rest) {
122
143
  filter = {
123
144
  ...filter,
124
145
  deleted: true
125
146
  };
126
- return this.exists(filter, ...rest);
147
+ return this.exists(filter, ...omitCallback(rest));
127
148
  });
128
149
  schema.static('countDocumentsDeleted', function countDocumentsDeleted(filter, ...rest) {
129
150
  filter = {
130
151
  ...filter,
131
152
  deleted: true
132
153
  };
133
- return this.countDocuments(filter, ...rest);
154
+ return this.countDocuments(filter, ...omitCallback(rest));
134
155
  });
135
156
  schema.static('findWithDeleted', function findWithDeleted(filter, ...rest) {
136
157
  filter = {
137
158
  ...filter,
138
159
  ...getWithDeletedQuery()
139
160
  };
140
- return this.find(filter, ...rest);
161
+ return this.find(filter, ...omitCallback(rest));
141
162
  });
142
163
  schema.static('findOneWithDeleted', function findOneWithDeleted(filter, ...rest) {
143
164
  filter = {
144
165
  ...filter,
145
166
  ...getWithDeletedQuery()
146
167
  };
147
- return this.findOne(filter, ...rest);
168
+ return this.findOne(filter, ...omitCallback(rest));
148
169
  });
149
170
  schema.static('findByIdWithDeleted', function findByIdWithDeleted(id, ...rest) {
150
171
  const filter = {
151
172
  _id: id,
152
173
  ...getWithDeletedQuery()
153
174
  };
154
- return this.findOne(filter, ...rest);
175
+ return this.findOne(filter, ...omitCallback(rest));
155
176
  });
156
177
  schema.static('existsWithDeleted', function existsWithDeleted(filter, ...rest) {
157
178
  filter = {
158
179
  ...filter,
159
180
  ...getWithDeletedQuery()
160
181
  };
161
- return this.exists(filter, ...rest);
182
+ return this.exists(filter, ...omitCallback(rest));
162
183
  });
163
184
  schema.static('countDocumentsWithDeleted', function countDocumentsWithDeleted(filter, ...rest) {
164
185
  filter = {
165
186
  ...filter,
166
187
  ...getWithDeletedQuery()
167
188
  };
168
- return this.countDocuments(filter, ...rest);
189
+ return this.countDocuments(filter, ...omitCallback(rest));
169
190
  });
170
191
  }
171
192
  function getDelete() {
@@ -368,4 +389,158 @@ function getCollisions(obj1, obj2) {
368
389
  return collisions;
369
390
  }
370
391
 
371
- // Disallowed Methods
392
+ // Hook Patch
393
+
394
+ function applyHookPatch(schema) {
395
+ const schemaPre = schema.pre;
396
+ const schemaPost = schema.post;
397
+ schema.pre = function (name, fn) {
398
+ if (name === 'restore') {
399
+ // Document hooks
400
+ schemaPre.call(this, 'save', getPreDocRestore(fn));
401
+ } else if (name === 'deleteOne') {
402
+ // Query Hooks
403
+ schemaPre.call(this, 'updateOne', getPreDelete(fn));
404
+ } else if (name === 'deleteMany') {
405
+ schemaPre.call(this, 'updateMany', getPreDelete(fn));
406
+ } else if (name === 'findOneAndDelete') {
407
+ schemaPre.call(this, 'findOneAndUpdate', getPreDelete(fn));
408
+ } else if (name === 'restoreOne') {
409
+ schemaPre.call(this, 'updateOne', getPreRestore(fn));
410
+ } else if (name === 'restoreMany') {
411
+ schemaPre.call(this, 'updateMany', getPreRestore(fn));
412
+ } else if (name === 'destroyOne') {
413
+ schemaPre.call(this, 'deleteOne', getPre(fn));
414
+ } else if (name === 'destroyMany') {
415
+ schemaPre.call(this, 'deleteMany', getPre(fn));
416
+ } else if (name === 'findDeleted') {
417
+ schemaPre.call(this, 'find', getPreDeleted(fn));
418
+ } else if (name === 'findOneDeleted') {
419
+ schemaPre.call(this, 'findOne', getPreDeleted(fn));
420
+ } else if (name === 'countDocumentsDeleted') {
421
+ schemaPre.call(this, 'countDocuments', getPreDeleted(fn));
422
+ } else if (name === 'findWithDeleted') {
423
+ schemaPre.call(this, 'find', getPreWithDeleted(fn));
424
+ } else if (name === 'findOneWithDeleted') {
425
+ schemaPre.call(this, 'findOne', getPreWithDeleted(fn));
426
+ } else if (name === 'countDocumentsWithDeleted') {
427
+ schemaPre.call(this, 'countDocuments', getPreWithDeleted(fn));
428
+ } else {
429
+ schemaPre.apply(this, arguments);
430
+ }
431
+ return this;
432
+ };
433
+ schema.post = function (name, fn) {
434
+ if (name === 'deleteOne') {
435
+ schemaPost.call(this, 'updateOne', getPostDelete(fn));
436
+ } else if (name === 'deleteMany') {
437
+ schemaPost.call(this, 'updateMany', getPostDelete(fn));
438
+ } else if (name === 'findOneAndDelete') {
439
+ schemaPost.call(this, 'findOneAndUpdate', getPostDelete(fn));
440
+ } else if (name === 'restoreOne') {
441
+ schemaPost.call(this, 'updateOne', getPostRestore(fn));
442
+ } else if (name === 'restoreMany') {
443
+ schemaPost.call(this, 'updateMany', getPostRestore(fn));
444
+ } else if (name === 'destroyOne') {
445
+ schemaPost.call(this, 'deleteOne', getPost(fn));
446
+ } else if (name === 'destroyMany') {
447
+ schemaPost.call(this, 'deleteMany', getPost(fn));
448
+ } else if (name === 'findDeleted') {
449
+ schemaPost.call(this, 'find', getPostDeleted(fn));
450
+ } else if (name === 'findOneDeleted') {
451
+ schemaPost.call(this, 'findOne', getPostDeleted(fn));
452
+ } else if (name === 'countDocumentsDeleted') {
453
+ schemaPost.call(this, 'countDocuments', getPostDeleted(fn));
454
+ } else if (name === 'findWithDeleted') {
455
+ schemaPost.call(this, 'find', getPostWithDeleted(fn));
456
+ } else if (name === 'findOneWithDeleted') {
457
+ schemaPost.call(this, 'findOne', getPostWithDeleted(fn));
458
+ } else if (name === 'countDocumentsWithDeleted') {
459
+ schemaPost.call(this, 'countDocuments', getPostWithDeleted(fn));
460
+ } else {
461
+ schemaPost.apply(this, arguments);
462
+ }
463
+ return this;
464
+ };
465
+ }
466
+
467
+ // Needs to be separated as hooks check arity to
468
+ // determine the arguments to pass.
469
+
470
+ function getPre(fn, check) {
471
+ return function (next) {
472
+ runHook(this, fn, check, next, arguments);
473
+ };
474
+ }
475
+ function getPost(fn, check) {
476
+ return function (res, next) {
477
+ runHook(this, fn, check, next, arguments);
478
+ };
479
+ }
480
+ function runHook(query, fn, check, next, args) {
481
+ if (!check || check(query)) {
482
+ const ret = fn.apply(query, args);
483
+ if (ret instanceof Promise) {
484
+ ret.finally(next);
485
+ }
486
+ } else {
487
+ next();
488
+ }
489
+ }
490
+ function getPreDelete(fn) {
491
+ return getPre(fn, query => {
492
+ return query.get('deleted') === true;
493
+ });
494
+ }
495
+ function getPreRestore(fn) {
496
+ return getPre(fn, query => {
497
+ return query.get('deleted') === false;
498
+ });
499
+ }
500
+ function getPreDeleted(fn) {
501
+ return getPre(fn, query => {
502
+ return query.getFilter().deleted === true;
503
+ });
504
+ }
505
+ function getPreWithDeleted(fn) {
506
+ return getPre(fn, query => {
507
+ return (0, _lodash.isEqual)(query.getFilter().deleted, getWithDeletedQuery().deleted);
508
+ });
509
+ }
510
+ function getPreDocRestore(fn) {
511
+ return getPre(fn, doc => {
512
+ return doc.isModified('deleted') && doc.deleted === false;
513
+ });
514
+ }
515
+ function getPostDelete(fn) {
516
+ return getPost(fn, query => {
517
+ return query.get('deleted') === true;
518
+ });
519
+ }
520
+ function getPostRestore(fn) {
521
+ return getPost(fn, query => {
522
+ return query.get('deleted') === false;
523
+ });
524
+ }
525
+ function getPostDeleted(fn) {
526
+ return getPost(fn, query => {
527
+ return query.getFilter().deleted === true;
528
+ });
529
+ }
530
+ function getPostWithDeleted(fn) {
531
+ return getPost(fn, query => {
532
+ return (0, _lodash.isEqual)(query.getFilter().deleted, getWithDeletedQuery().deleted);
533
+ });
534
+ }
535
+
536
+ // Utils
537
+
538
+ // Mongoose >= v7 no longer accepts a callback for queries,
539
+ // however it still passes post hooks to static methods for
540
+ // some reason (this appears to be a bug), so omit functions
541
+ // here to allow projectsion/options to still be passed.
542
+ function omitCallback(args) {
543
+ return args.filter(arg => {
544
+ return typeof arg !== 'function';
545
+ });
546
+ }
@@ -23,11 +23,10 @@ const DATE_TAGS = {
23
23
  'x-schema': 'DateTime',
24
24
  'x-description': 'A `string` in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.'
25
25
  };
26
- const OBJECT_ID_SCHEMA = _yada.default.string().mongo().message('Must be an ObjectId.').tag({
26
+ const OBJECT_ID_SCHEMA = exports.OBJECT_ID_SCHEMA = _yada.default.string().mongo().message('Must be an ObjectId.').tag({
27
27
  'x-schema': 'ObjectId',
28
28
  'x-description': 'A 24 character hexadecimal string representing a Mongo [ObjectId](https://bit.ly/3YPtGlU).'
29
29
  });
30
- exports.OBJECT_ID_SCHEMA = OBJECT_ID_SCHEMA;
31
30
  const REFERENCE_SCHEMA = _yada.default.allow(OBJECT_ID_SCHEMA, _yada.default.object({
32
31
  id: OBJECT_ID_SCHEMA.required()
33
32
  }).options({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/model",
3
- "version": "0.1.33",
3
+ "version": "0.2.0",
4
4
  "description": "Bedrock utilities for model creation.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "peerDependencies": {
32
32
  "@bedrockio/yada": "^1.0.33",
33
- "mongoose": ">=6.9.0"
33
+ "mongoose": "^6.9.0 || ^7.6.4"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@babel/cli": "^7.20.7",
@@ -38,14 +38,14 @@
38
38
  "@babel/preset-env": "^7.20.2",
39
39
  "@bedrockio/prettier-config": "^1.0.2",
40
40
  "@bedrockio/yada": "^1.0.33",
41
- "@shelf/jest-mongodb": "^4.1.6",
41
+ "@shelf/jest-mongodb": "^4.1.7",
42
42
  "babel-plugin-import-replacement": "^1.0.1",
43
43
  "eslint": "^8.33.0",
44
44
  "eslint-plugin-bedrock": "^1.0.26",
45
45
  "jest": "^29.4.1",
46
46
  "jest-environment-node": "^29.4.1",
47
- "mongodb": "4.13.0",
48
- "mongoose": "^6.9.0",
47
+ "mongodb": "^5.9.1",
48
+ "mongoose": "^7.6.4",
49
49
  "prettier-eslint": "^15.0.1",
50
50
  "typescript": "^4.9.5"
51
51
  },