@bedrockio/model 0.3.2 → 0.4.1

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.
@@ -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
+ }
package/dist/cjs/query.js CHANGED
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.mergeQuery = mergeQuery;
6
7
  exports.wrapQuery = wrapQuery;
7
8
  // Wrapper that allows a mongoose query object to be returned
8
9
  // to access chained methods while still resolving with a
@@ -18,4 +19,20 @@ function wrapQuery(query, fn) {
18
19
  }
19
20
  };
20
21
  return query;
22
+ }
23
+ function mergeQuery(target, source) {
24
+ const result = Object.assign({}, target, source);
25
+ if (target.$or && source.$or) {
26
+ const {
27
+ $or
28
+ } = target;
29
+ result.$or = source.$or.map(conditions => {
30
+ return {
31
+ $and: [conditions, {
32
+ $or
33
+ }]
34
+ };
35
+ });
36
+ }
37
+ return result;
21
38
  }
@@ -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;
@@ -25,10 +25,14 @@ const {
25
25
  function applySearch(schema, definition) {
26
26
  validateDefinition(definition);
27
27
  applySearchCache(schema, definition);
28
+ const {
29
+ query: searchQuery,
30
+ fields: searchFields
31
+ } = definition.search || {};
28
32
  schema.static('search', function search(body = {}) {
29
33
  const options = {
30
34
  ..._const.SEARCH_DEFAULTS,
31
- ...definition.search?.query,
35
+ ...searchQuery,
32
36
  ...body
33
37
  };
34
38
  const {
@@ -39,16 +43,19 @@ function applySearch(schema, definition) {
39
43
  sort,
40
44
  ...rest
41
45
  } = options;
42
- const query = {};
46
+ let query = normalizeQuery(rest, schema.obj);
43
47
  if (ids?.length) {
44
- query._id = {
45
- $in: ids
48
+ query = {
49
+ ...query,
50
+ _id: {
51
+ $in: ids
52
+ }
46
53
  };
47
54
  }
48
55
  if (keyword) {
49
- Object.assign(query, buildKeywordQuery(schema, keyword, definition.search?.fields));
56
+ const keywordQuery = buildKeywordQuery(schema, keyword, searchFields);
57
+ query = (0, _query.mergeQuery)(query, keywordQuery);
50
58
  }
51
- Object.assign(query, normalizeQuery(rest, schema.obj));
52
59
  if (_env.debug) {
53
60
  _logger.default.info(`Search query for ${this.modelName}:\n`, JSON.stringify(query, null, 2));
54
61
  }
@@ -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.2",
3
+ "version": "0.4.1",
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",
@@ -45,7 +45,7 @@
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/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/query.js CHANGED
@@ -13,3 +13,16 @@ export function wrapQuery(query, fn) {
13
13
  };
14
14
  return query;
15
15
  }
16
+
17
+ export function mergeQuery(target, source) {
18
+ const result = Object.assign({}, target, source);
19
+ if (target.$or && source.$or) {
20
+ const { $or } = target;
21
+ result.$or = source.$or.map((conditions) => {
22
+ return {
23
+ $and: [conditions, { $or }],
24
+ };
25
+ });
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/search.js CHANGED
@@ -15,7 +15,7 @@ import { isDateField, isNumberField, getField } from './utils';
15
15
  import { SEARCH_DEFAULTS } from './const';
16
16
  import { OBJECT_ID_SCHEMA } from './validation';
17
17
  import { debug } from './env';
18
- import { wrapQuery } from './query';
18
+ import { mergeQuery, wrapQuery } from './query';
19
19
 
20
20
  import warn from './warn';
21
21
 
@@ -26,30 +26,31 @@ export function applySearch(schema, definition) {
26
26
  validateDefinition(definition);
27
27
  applySearchCache(schema, definition);
28
28
 
29
+ const { query: searchQuery, fields: searchFields } = definition.search || {};
30
+
29
31
  schema.static('search', function search(body = {}) {
30
32
  const options = {
31
33
  ...SEARCH_DEFAULTS,
32
- ...definition.search?.query,
34
+ ...searchQuery,
33
35
  ...body,
34
36
  };
35
37
 
36
38
  const { ids, keyword, skip = 0, limit, sort, ...rest } = options;
37
39
 
38
- const query = {};
40
+ let query = normalizeQuery(rest, schema.obj);
39
41
 
40
42
  if (ids?.length) {
41
- query._id = { $in: ids };
43
+ query = {
44
+ ...query,
45
+ _id: { $in: ids },
46
+ };
42
47
  }
43
48
 
44
49
  if (keyword) {
45
- Object.assign(
46
- query,
47
- buildKeywordQuery(schema, keyword, definition.search?.fields)
48
- );
50
+ const keywordQuery = buildKeywordQuery(schema, keyword, searchFields);
51
+ query = mergeQuery(query, keywordQuery);
49
52
  }
50
53
 
51
- Object.assign(query, normalizeQuery(rest, schema.obj));
52
-
53
54
  if (debug) {
54
55
  logger.info(
55
56
  `Search query for ${this.modelName}:\n`,
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) {
@@ -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"}
@@ -14,7 +14,7 @@ export const INCLUDE_FIELD_SCHEMA: {
14
14
  meta: {};
15
15
  required(): any;
16
16
  default(arg: any): any;
17
- custom(fn: Function): any;
17
+ custom(...args: import("@bedrockio/yada/types/Schema").CustomSignature): any;
18
18
  strip(strip: any): any;
19
19
  allow(...set: any[]): any;
20
20
  reject(...set: any[]): any;
package/types/query.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export function wrapQuery(query: any, fn: any): any;
2
+ export function mergeQuery(target: any, source: any): any;
2
3
  //# sourceMappingURL=query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.js"],"names":[],"mappings":"AAIA,oDAUC"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.js"],"names":[],"mappings":"AAIA,oDAUC;AAED,0DAWC"}
package/types/schema.d.ts CHANGED
@@ -8,7 +8,6 @@
8
8
  */
9
9
  export function createSchema(definition: object, options?: mongoose.SchemaOptions): mongoose.Schema<any, mongoose.Model<any, any, any, any, any, any>, any, any, any, any, {
10
10
  autoIndex?: boolean;
11
- autoSearchIndex?: boolean;
12
11
  autoCreate?: boolean;
13
12
  bufferCommands?: boolean;
14
13
  bufferTimeoutMS?: number;
@@ -31,9 +30,6 @@ export function createSchema(definition: object, options?: mongoose.SchemaOption
31
30
  optimisticConcurrency?: boolean;
32
31
  pluginTags?: string[];
33
32
  read?: string;
34
- readConcern?: {
35
- level: "local" | "available" | "majority" | "snapshot" | "linearizable";
36
- };
37
33
  writeConcern?: mongoose.mongo.WriteConcern;
38
34
  safe?: boolean | {
39
35
  w?: string | number;
@@ -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"}
package/types/search.d.ts CHANGED
@@ -11,7 +11,7 @@ export function searchValidation(options?: {}): {
11
11
  meta: {};
12
12
  required(): any;
13
13
  default(arg: any): any;
14
- custom(fn: Function): any;
14
+ custom(...args: import("@bedrockio/yada/types/Schema").CustomSignature): any;
15
15
  strip(strip: any): any;
16
16
  allow(...set: any[]): any;
17
17
  reject(...set: any[]): any;
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAwBA,gEAuDC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBC"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAwBA,gEAwDC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBC"}
@@ -11,7 +11,6 @@ export function getTupleValidator(types: any): {
11
11
  };
12
12
  export const OBJECT_ID_SCHEMA: {
13
13
  required(): any;
14
- allowEmpty(): any;
15
14
  length(length: number): any;
16
15
  min(length: number): any;
17
16
  max(length: number): any;
@@ -73,7 +72,7 @@ export const OBJECT_ID_SCHEMA: {
73
72
  assertions: any[];
74
73
  meta: {};
75
74
  default(arg: any): any;
76
- custom(fn: Function): any;
75
+ custom(...args: import("@bedrockio/yada/types/Schema").CustomSignature): any;
77
76
  strip(strip: any): any;
78
77
  allow(...set: any[]): any;
79
78
  reject(...set: any[]): any;
@@ -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"}