@bedrockio/model 0.3.1 → 0.4.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.
@@ -5,14 +5,15 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.applyAssign = applyAssign;
7
7
  var _lodash = require("lodash");
8
+ var _mongoose = _interopRequireDefault(require("mongoose"));
8
9
  var _utils = require("./utils");
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
11
  function applyAssign(schema) {
10
12
  schema.method('assign', function assign(fields) {
11
13
  unsetReferenceFields(fields, schema.obj);
12
14
  for (let [path, value] of Object.entries(flattenObject(fields))) {
13
15
  if (value === null) {
14
16
  this.set(path, undefined);
15
- this.markModified(path);
16
17
  } else {
17
18
  this.set(path, value);
18
19
  }
@@ -27,6 +28,8 @@ function unsetReferenceFields(fields, schema = {}) {
27
28
  for (let [key, value] of Object.entries(fields)) {
28
29
  if (!value && (0, _utils.isReferenceField)(schema, key)) {
29
30
  fields[key] = undefined;
31
+ } else if (value instanceof _mongoose.default.Document) {
32
+ fields[key] = value;
30
33
  } else if (value && typeof value === 'object') {
31
34
  unsetReferenceFields(value, (0, _utils.getField)(schema, key));
32
35
  }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.applyHydrate = applyHydrate;
7
+ var _mongoose = require("mongoose");
8
+ // Patching mongoose hydrate method to be less useless
9
+ // as it sets all input fields on the resulting document.
10
+ // Compare this to creating a new model which will only
11
+ // set the fields known to the schema. This results in:
12
+ //
13
+ // 1. Sending too much data down the wire
14
+ // 2. Potentially leaking sensitive data in aggregations.
15
+
16
+ function applyHydrate(schema) {
17
+ schema.static('hydrate', function hydrate(obj) {
18
+ return _mongoose.Model.hydrate.call(this, obj, getProjection(schema));
19
+ });
20
+ }
21
+
22
+ // Note that the mongoose docs imply that an array of
23
+ // strings will work for the projection, however this
24
+ // does not seem to work.
25
+ // https://mongoosejs.com/docs/7.x/docs/api/model.html
26
+ function getProjection(schema) {
27
+ const keys = Object.keys(schema.paths);
28
+ const result = {};
29
+ for (let key of keys) {
30
+ result[key] = true;
31
+ }
32
+ return result;
33
+ }
@@ -12,6 +12,7 @@ var _serialization = require("./serialization");
12
12
  var _slug = require("./slug");
13
13
  var _search = require("./search");
14
14
  var _assign = require("./assign");
15
+ var _hydrate = require("./hydrate");
15
16
  var _include = require("./include");
16
17
  var _softDelete = require("./soft-delete");
17
18
  var _deleteHooks = require("./delete-hooks");
@@ -54,6 +55,7 @@ function createSchema(definition, options = {}) {
54
55
  (0, _search.applySearch)(schema, definition);
55
56
  (0, _disallowed.applyDisallowed)(schema);
56
57
  (0, _include.applyInclude)(schema);
58
+ (0, _hydrate.applyHydrate)(schema);
57
59
  (0, _assign.applyAssign)(schema);
58
60
  (0, _slug.applySlug)(schema);
59
61
  return schema;
@@ -282,11 +282,14 @@ function getSchemaForTypedef(typedef, options = {}) {
282
282
  schema = getObjectSchema(type, options);
283
283
  } else {
284
284
  schema = getSchemaForType(type, options);
285
+
286
+ // Unsetting only allowed for primitive types.
287
+ if (allowUnset(typedef, options)) {
288
+ schema = _yada.default.allow(null, '', schema);
289
+ }
285
290
  }
286
291
  if (isRequired(typedef, options)) {
287
292
  schema = schema.required();
288
- } else if (allowUnset(typedef, options)) {
289
- schema = _yada.default.allow(null, '', schema);
290
293
  }
291
294
  if (typedef.default && options.allowDefaultTags) {
292
295
  // Tag the default value to allow OpenAPI description
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/model",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Bedrock utilities for model creation.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@bedrockio/yada": "^1.0.40",
34
- "mongoose": "^7.6.4"
34
+ "mongoose": "^7.6.13"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@babel/cli": "^7.20.7",
@@ -39,13 +39,13 @@
39
39
  "@babel/preset-env": "^7.20.2",
40
40
  "@bedrockio/prettier-config": "^1.0.2",
41
41
  "@bedrockio/yada": "^1.0.40",
42
- "@shelf/jest-mongodb": "^4.2.0",
42
+ "@shelf/jest-mongodb": "^4.3.2",
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
47
  "mongodb": "^6.5.0",
48
- "mongoose": "^7.6.4",
48
+ "mongoose": "7.6.13",
49
49
  "prettier-eslint": "^15.0.1",
50
50
  "typescript": "^4.9.5"
51
51
  },
package/src/assign.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { isPlainObject } from 'lodash';
2
+ import mongoose from 'mongoose';
2
3
 
3
4
  import { isReferenceField, getField } from './utils';
4
5
 
@@ -8,7 +9,6 @@ export function applyAssign(schema) {
8
9
  for (let [path, value] of Object.entries(flattenObject(fields))) {
9
10
  if (value === null) {
10
11
  this.set(path, undefined);
11
- this.markModified(path);
12
12
  } else {
13
13
  this.set(path, value);
14
14
  }
@@ -23,6 +23,8 @@ function unsetReferenceFields(fields, schema = {}) {
23
23
  for (let [key, value] of Object.entries(fields)) {
24
24
  if (!value && isReferenceField(schema, key)) {
25
25
  fields[key] = undefined;
26
+ } else if (value instanceof mongoose.Document) {
27
+ fields[key] = value;
26
28
  } else if (value && typeof value === 'object') {
27
29
  unsetReferenceFields(value, getField(schema, key));
28
30
  }
package/src/hydrate.js ADDED
@@ -0,0 +1,28 @@
1
+ import { Model } from 'mongoose';
2
+
3
+ // Patching mongoose hydrate method to be less useless
4
+ // as it sets all input fields on the resulting document.
5
+ // Compare this to creating a new model which will only
6
+ // set the fields known to the schema. This results in:
7
+ //
8
+ // 1. Sending too much data down the wire
9
+ // 2. Potentially leaking sensitive data in aggregations.
10
+
11
+ export function applyHydrate(schema) {
12
+ schema.static('hydrate', function hydrate(obj) {
13
+ return Model.hydrate.call(this, obj, getProjection(schema));
14
+ });
15
+ }
16
+
17
+ // Note that the mongoose docs imply that an array of
18
+ // strings will work for the projection, however this
19
+ // does not seem to work.
20
+ // https://mongoosejs.com/docs/7.x/docs/api/model.html
21
+ function getProjection(schema) {
22
+ const keys = Object.keys(schema.paths);
23
+ const result = {};
24
+ for (let key of keys) {
25
+ result[key] = true;
26
+ }
27
+ return result;
28
+ }
package/src/schema.js CHANGED
@@ -7,6 +7,7 @@ import { serializeOptions } from './serialization';
7
7
  import { applySlug } from './slug';
8
8
  import { applySearch } from './search';
9
9
  import { applyAssign } from './assign';
10
+ import { applyHydrate } from './hydrate';
10
11
  import { applyInclude } from './include';
11
12
  import { applySoftDelete } from './soft-delete';
12
13
  import { applyDeleteHooks } from './delete-hooks';
@@ -58,6 +59,7 @@ export function createSchema(definition, options = {}) {
58
59
  applySearch(schema, definition);
59
60
  applyDisallowed(schema);
60
61
  applyInclude(schema);
62
+ applyHydrate(schema);
61
63
  applyAssign(schema);
62
64
  applySlug(schema);
63
65
 
package/src/validation.js CHANGED
@@ -297,12 +297,15 @@ function getSchemaForTypedef(typedef, options = {}) {
297
297
  schema = getObjectSchema(type, options);
298
298
  } else {
299
299
  schema = getSchemaForType(type, options);
300
+
301
+ // Unsetting only allowed for primitive types.
302
+ if (allowUnset(typedef, options)) {
303
+ schema = yd.allow(null, '', schema);
304
+ }
300
305
  }
301
306
 
302
307
  if (isRequired(typedef, options)) {
303
308
  schema = schema.required();
304
- } else if (allowUnset(typedef, options)) {
305
- schema = yd.allow(null, '', schema);
306
309
  }
307
310
 
308
311
  if (typedef.default && options.allowDefaultTags) {
@@ -1 +1 @@
1
- {"version":3,"file":"assign.d.ts","sourceRoot":"","sources":["../src/assign.js"],"names":[],"mappings":"AAIA,+CAYC"}
1
+ {"version":3,"file":"assign.d.ts","sourceRoot":"","sources":["../src/assign.js"],"names":[],"mappings":"AAKA,+CAWC"}
@@ -0,0 +1,2 @@
1
+ export function applyHydrate(schema: any): void;
2
+ //# sourceMappingURL=hydrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../src/hydrate.js"],"names":[],"mappings":"AAUA,gDAIC"}
package/types/load.d.ts CHANGED
@@ -4,7 +4,73 @@
4
4
  * @param {string} name
5
5
  * @returns mongoose.Model
6
6
  */
7
- export function loadModel(definition: object, name: string): any;
7
+ export function loadModel(definition: object, name: string): mongoose.Model<any, any, any, any, any, mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, any, any, any, {
8
+ [x: string]: any;
9
+ }, {
10
+ autoIndex?: boolean;
11
+ autoCreate?: boolean;
12
+ bufferCommands?: boolean;
13
+ bufferTimeoutMS?: number;
14
+ capped?: number | boolean | {
15
+ size?: number;
16
+ max?: number;
17
+ autoIndexId?: boolean;
18
+ };
19
+ collation?: mongoose.mongo.CollationOptions;
20
+ collectionOptions?: mongoose.mongo.CreateCollectionOptions;
21
+ timeseries?: mongoose.mongo.TimeSeriesCollectionOptions;
22
+ expireAfterSeconds?: number;
23
+ expires?: string | number;
24
+ collection?: string;
25
+ discriminatorKey?: string;
26
+ excludeIndexes?: boolean;
27
+ id?: boolean;
28
+ _id?: boolean;
29
+ minimize?: boolean;
30
+ optimisticConcurrency?: boolean;
31
+ pluginTags?: string[];
32
+ read?: string;
33
+ writeConcern?: mongoose.mongo.WriteConcern;
34
+ safe?: boolean | {
35
+ w?: string | number;
36
+ wtimeout?: number;
37
+ j?: boolean;
38
+ };
39
+ shardKey?: Record<string, unknown>;
40
+ strict?: boolean | "throw";
41
+ strictQuery?: boolean | "throw";
42
+ toJSON: {
43
+ getters: boolean;
44
+ versionKey: boolean;
45
+ transform: (doc: any, ret: any, options: any) => void;
46
+ } | mongoose.ToObjectOptions<any>;
47
+ toObject: {
48
+ getters: boolean;
49
+ versionKey: boolean;
50
+ transform: (doc: any, ret: any, options: any) => void;
51
+ } | mongoose.ToObjectOptions<any>;
52
+ typeKey?: string;
53
+ validateBeforeSave?: boolean;
54
+ validateModifiedOnly?: boolean;
55
+ versionKey?: string | boolean;
56
+ selectPopulatedPaths?: boolean;
57
+ skipVersioning?: {
58
+ [key: string]: boolean;
59
+ };
60
+ storeSubdocValidationError?: boolean;
61
+ timestamps: boolean | mongoose.SchemaTimestampsConfig;
62
+ suppressReservedKeysWarning?: boolean;
63
+ statics?: {
64
+ [x: string]: any;
65
+ };
66
+ methods?: any;
67
+ query?: any;
68
+ castNonArrays?: boolean;
69
+ virtuals?: mongoose.SchemaOptionsVirtualsPropertyType<any, any, any>;
70
+ overwriteModels?: boolean;
71
+ }, any, any>> & {
72
+ [x: string]: any;
73
+ };
8
74
  /**
9
75
  * Loads all model definitions in the given directory.
10
76
  * Returns the full loaded model set.
@@ -1 +1 @@
1
- {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.js"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,sCAJW,MAAM,QACN,MAAM,OAahB;AAED;;;;GAIG;AACH,sCAFW,MAAM,mBAkBhB"}
1
+ {"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.js"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,sCAJW,MAAM,QACN,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAahB;AAED;;;;GAIG;AACH,sCAFW,MAAM,mBAkBhB"}
package/types/schema.d.ts CHANGED
@@ -6,9 +6,10 @@
6
6
  * @param {mongoose.SchemaOptions} options
7
7
  * @returns mongoose.Schema
8
8
  */
9
- export function createSchema(definition: object, options?: mongoose.SchemaOptions): mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, any, any, any, any, {
9
+ export function createSchema(definition: object, options?: mongoose.SchemaOptions): mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, any, any, any, {
10
+ [x: string]: any;
11
+ }, {
10
12
  autoIndex?: boolean;
11
- autoSearchIndex?: boolean;
12
13
  autoCreate?: boolean;
13
14
  bufferCommands?: boolean;
14
15
  bufferTimeoutMS?: number;
@@ -61,8 +62,10 @@ export function createSchema(definition: object, options?: mongoose.SchemaOption
61
62
  storeSubdocValidationError?: boolean;
62
63
  timestamps: boolean | mongoose.SchemaTimestampsConfig;
63
64
  suppressReservedKeysWarning?: boolean;
64
- statics?: mongoose.AddThisParameter<any, mongoose.Model<any, {}, {}, {}, any, any>>;
65
- methods?: mongoose.AddThisParameter<any, any> & mongoose.AnyObject;
65
+ statics?: {
66
+ [x: string]: any;
67
+ };
68
+ methods?: any;
66
69
  query?: any;
67
70
  castNonArrays?: boolean;
68
71
  virtuals?: mongoose.SchemaOptionsVirtualsPropertyType<any, any, any>;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.js"],"names":[],"mappings":"AAoBA;;;;;;;GAOG;AACH,yCAJW,MAAM,YACN,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAuChC;AAED,iEAsBC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.js"],"names":[],"mappings":"AAqBA;;;;;;;GAOG;AACH,yCAJW,MAAM,YACN,SAAS,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAwChC;AAED,iEAsBC"}
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.js"],"names":[],"mappings":"AAkFA,kDAEC;AAED,oEA8FC;AAsBD,wEA2BC;AAgSD;;;EAEC;AAED;;;EAOC;AA9fD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQK"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.js"],"names":[],"mappings":"AAkFA,kDAEC;AAED,oEA8FC;AAsBD,wEA2BC;AAmSD;;;EAEC;AAED;;;EAOC;AAjgBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQK"}