cosa 6.3.0 → 7.0.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2022 Losant IoT, Inc.
3
+ Copyright (c) 2023 Losant IoT, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/lib/db.js CHANGED
@@ -342,6 +342,7 @@ class Database extends EventEmitter {
342
342
  * @see {@link https://mongodb.github.io/node-mongodb-native/4.0/interfaces/aggregateoptions.html#maxtimems}
343
343
  */
344
344
  async aggregate(collectionName, pipeline, options = {}) {
345
+ pipeline = deserialize(pipeline);
345
346
  const collection = await this.collection(collectionName);
346
347
  return collection.aggregate(pipeline, {
347
348
  readPreference: options.readPreference,
package/lib/model.js CHANGED
@@ -1,4 +1,4 @@
1
- const joi = require('@hapi/joi');
1
+ const joi = require('joi');
2
2
  const etag = require('etag');
3
3
  const debug = require('debug')('cosa:model');
4
4
  const objectPath = require('object-path');
@@ -7,7 +7,9 @@ const { EJSON } = require('bson');
7
7
  const Cursor = require('./cursor');
8
8
  const errors = require('./errors');
9
9
  const { ObjectId } = require('bson');
10
- const { pathEq, complement, pick, omit } = require('omnibelt');
10
+ const {
11
+ pathEq, complement, pick, omit, has, isEmpty, isPlainObject
12
+ } = require('omnibelt');
11
13
  const { buildPropertySchema } = require('./utils');
12
14
  const Immutable = require('./immutable');
13
15
  Immutable.use(require('./defined-object'));
@@ -23,6 +25,16 @@ const db = require('./db');
23
25
  // functions that are defined later on.
24
26
  let _serialize, _extend;
25
27
 
28
+ const shouldWait = (waitType) => {
29
+ return (options, definition) => {
30
+ if (has(waitType, options)) { return options[waitType]; }
31
+ return definition[waitType];
32
+ };
33
+ };
34
+
35
+ const shouldWaitAfterSave = shouldWait('waitAfterSave');
36
+ const shouldWaitAfterRemove = shouldWait('waitAfterRemove');
37
+
26
38
  const addVirtuals = (def, obj) => {
27
39
  if (def.type === 'array' && def.items && Array.isArray(obj)) {
28
40
  for (let i = 0, l = obj.length; i < l; i++) {
@@ -177,7 +189,7 @@ const _create = (data, definition) => {
177
189
  if ('function' === typeof newOrUpdatedModel.afterSaveCommit) {
178
190
  chain = chain.then(() => newOrUpdatedModel.afterSaveCommit(original, options));
179
191
  }
180
- if (options.waitAfterSave) {
192
+ if (shouldWaitAfterSave(options, definition)) {
181
193
  await chain;
182
194
  }
183
195
  }
@@ -233,11 +245,16 @@ const _create = (data, definition) => {
233
245
  });
234
246
  };
235
247
 
236
- definition.methods.del = function(path) {
248
+ definition.methods.del = function(toDelete) {
237
249
  const original = this;
238
250
  return this.mutate(function() {
239
- objectPath.del(this, path);
240
- _markAsModified(this, [path], original);
251
+ if (!Array.isArray(toDelete)) {
252
+ toDelete = [toDelete];
253
+ }
254
+ toDelete.forEach((path) => {
255
+ objectPath.del(this, path);
256
+ });
257
+ _markAsModified(this, toDelete, original);
241
258
  });
242
259
  };
243
260
 
@@ -333,7 +350,7 @@ const _create = (data, definition) => {
333
350
  if ('function' === typeof this.afterRemoveCommit) {
334
351
  chain = chain.then(() => this.afterRemoveCommit(options));
335
352
  }
336
- if (options.waitAfterRemove) {
353
+ if (shouldWaitAfterRemove(options, definition)) {
337
354
  await chain;
338
355
  }
339
356
  }
@@ -399,9 +416,17 @@ const define = (definition) => {
399
416
 
400
417
  definition._schema = _buildSchema(definition);
401
418
 
402
- const applyGlobalWhere = (query, options) => {
419
+ const makeSafeQuery = (query, options) => {
420
+ if (!query || !isPlainObject(query)) {
421
+ throw new Error('Query must be an object.');
422
+ }
423
+
424
+ if (!options.allowGlobalQuery && isEmpty(query)) {
425
+ throw new Error('To make an unrestricted query, please set the allowGlobalQuery option.');
426
+ }
427
+
403
428
  return options.bypassGlobalWhere || !definition.where ?
404
- query : { ...definition.where, ...(query || {}) };
429
+ query : { ...definition.where, ...query };
405
430
  };
406
431
 
407
432
  return {
@@ -422,15 +447,15 @@ const define = (definition) => {
422
447
  return Immutable.isImmutableType(obj, definition.name || 'object');
423
448
  },
424
449
 
425
- count: (query, options = {}) => {
426
- query = applyGlobalWhere(query, options);
450
+ count: async (query, options = {}) => {
451
+ query = makeSafeQuery(query, options);
427
452
  const collection = definition.collection;
428
453
  debug(`counting ${JSON.stringify(query)} in ${collection}`);
429
454
  return db.find(collection, query, { ...options, count: true });
430
455
  },
431
456
 
432
457
  find: async (query, options = {}) => {
433
- query = applyGlobalWhere(query, options);
458
+ query = makeSafeQuery(query, options);
434
459
  const collection = definition.collection;
435
460
  debug(`finding ${JSON.stringify(query)} in ${collection}`);
436
461
  const dbCursor = await db.find(collection, query, options);
@@ -440,16 +465,22 @@ const define = (definition) => {
440
465
  return options.array ? cursor.toArray() : cursor;
441
466
  },
442
467
 
468
+ exists: async (query, options = {}) => {
469
+ query = makeSafeQuery(query, options);
470
+ const collection = definition.collection;
471
+ return !!(await db.find(collection, query, { ...options, count: true, limit: 1 }));
472
+ },
473
+
443
474
  findOne: async (query, options = {}) => {
444
- query = applyGlobalWhere(query, options);
475
+ query = makeSafeQuery(query, options);
445
476
  const collection = definition.collection;
446
477
  debug(`finding one${JSON.stringify(query)} in ${collection}`);
447
478
  const result = await db.find(collection, query, { ...options, findOne: true });
448
479
  return !result ? null : _create(result, definition);
449
480
  },
450
481
 
451
- update: (query, update, { autoSet = true, ...options } = {}) => {
452
- query = applyGlobalWhere(query, options);
482
+ update: async (query, update, { autoSet = true, ...options } = {}) => {
483
+ query = makeSafeQuery(query, options);
453
484
  const collection = definition.collection;
454
485
  if (autoSet) {
455
486
  update = { $set: update };
@@ -458,28 +489,40 @@ const define = (definition) => {
458
489
  return db.update(collection, query, update, options);
459
490
  },
460
491
 
461
- remove: (query, options = {}) => {
462
- query = applyGlobalWhere(query, options);
492
+ remove: async (query, options = {}) => {
493
+ query = makeSafeQuery(query, options);
463
494
  const collection = definition.collection;
464
495
  debug(`removing ${JSON.stringify(query)} from ${collection}`);
465
496
  return db.remove(collection, query, options);
466
497
  },
467
498
 
468
- distinct: (key, query, options = {}) => {
469
- query = applyGlobalWhere(query, options);
499
+ distinct: async (key, query, options = {}) => {
500
+ query = makeSafeQuery(query, options);
470
501
  const collection = definition.collection;
471
502
  debug(`finding distinct "${key}" ${JSON.stringify(query)} from ${collection}`);
472
503
  return db.distinct(collection, key, query, options);
473
504
  },
474
505
 
475
- aggregate: (pipeline, options = {}) => {
506
+ aggregate: async (pipeline, options = {}) => {
507
+ if (!Array.isArray(pipeline)) {
508
+ throw new Error('Aggregation pipeline must be an array.');
509
+ }
510
+
511
+ pipeline = pipeline.slice();
512
+ if (pipeline[0]?.$match) {
513
+ pipeline[0] = { ...pipeline[0], $match: makeSafeQuery(pipeline[0].$match, options) };
514
+ } else {
515
+ pipeline.unshift({ $match: makeSafeQuery({}, options) });
516
+ }
517
+
476
518
  const collection = definition.collection;
477
519
  debug(`aggregating ${JSON.stringify(pipeline)} from ${collection}`);
478
- return db.aggregate(collection, pipeline, options);
520
+ const dbCursor = await db.aggregate(collection, pipeline, options);
521
+ return options.array ? dbCursor.toArray() : dbCursor;
479
522
  },
480
523
 
481
524
  project: async (query, value, options = {}) => {
482
- query = applyGlobalWhere(query, options);
525
+ query = makeSafeQuery(query, options);
483
526
  const collection = definition.collection;
484
527
  debug(`project ${value}, ${JSON.stringify(query)} in ${collection}`);
485
528
  let dbCursor = await db.find(collection, query, options);
package/lib/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- const joi = require('@hapi/joi');
1
+ const joi = require('joi');
2
2
  const { ObjectId } = require('bson');
3
3
  const buildPropertySchema = (name, propertyDef) => { // eslint-disable-line complexity
4
4
  let schema = null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cosa",
3
- "version": "6.3.0",
3
+ "version": "7.0.0",
4
4
  "description": "Cosa Models for MongoDB",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -35,25 +35,25 @@
35
35
  "lib"
36
36
  ],
37
37
  "dependencies": {
38
- "bson": "~4.7.0",
38
+ "bson": "~4.7.2",
39
39
  "clone": "^2.1.2",
40
40
  "debug": "^4.3.4",
41
41
  "error": "^7.0.2",
42
42
  "etag": "^1.8.1",
43
- "@hapi/joi": "^17.1.1",
44
- "mongodb": "~4.10.0",
43
+ "joi": "^17.8.3",
44
+ "mongodb": "~4.14.0",
45
45
  "object-path": "^0.11.8",
46
46
  "omnibelt": "^2.1.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@losant/eslint-config-losant": "^1.4.4",
50
- "husky": "^8.0.1",
51
- "lint-staged": "^13.0.3",
52
- "chai": "^4.3.6",
49
+ "@losant/eslint-config-losant": "^1.6.0",
50
+ "husky": "^8.0.3",
51
+ "lint-staged": "^13.1.2",
52
+ "chai": "^4.3.7",
53
53
  "chai-as-promised": "^7.1.1",
54
54
  "chai-datetime": "^1.8.0",
55
- "documentation": "^14.0.0",
56
- "mocha": "^10.0.0",
55
+ "documentation": "^14.0.1",
56
+ "mocha": "^10.2.0",
57
57
  "string-template": "^1.0.0"
58
58
  },
59
59
  "eslintConfig": {