@bedrockio/model 0.1.8 → 0.1.10
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/dist/cjs/schema.js +0 -4
- package/dist/cjs/search.js +31 -15
- package/dist/cjs/validation.js +65 -35
- package/package.json +1 -1
- package/src/schema.js +0 -7
- package/src/search.js +28 -16
- package/src/validation.js +92 -47
- package/types/schema.d.ts +0 -1
- package/types/schema.d.ts.map +1 -1
- package/types/search.d.ts +1 -1
- package/types/search.d.ts.map +1 -1
- package/types/validation.d.ts +91 -1
- package/types/validation.d.ts.map +1 -1
- package/yarn-error.log +979 -1038
package/dist/cjs/schema.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.RESERVED_FIELDS = void 0;
|
|
7
6
|
exports.createSchema = createSchema;
|
|
8
7
|
exports.normalizeAttributes = normalizeAttributes;
|
|
9
8
|
var _mongoose = _interopRequireDefault(require("mongoose"));
|
|
@@ -19,8 +18,6 @@ var _softDelete = require("./soft-delete");
|
|
|
19
18
|
var _disallowed = require("./disallowed");
|
|
20
19
|
var _validation = require("./validation");
|
|
21
20
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
22
|
-
const RESERVED_FIELDS = ['createdAt', 'updatedAt', 'deletedAt', 'deleted'];
|
|
23
|
-
|
|
24
21
|
/**
|
|
25
22
|
* Creates a new Mongoose schema with Bedrock extensions
|
|
26
23
|
* applied. For more about syntax and functionality see
|
|
@@ -29,7 +26,6 @@ const RESERVED_FIELDS = ['createdAt', 'updatedAt', 'deletedAt', 'deleted'];
|
|
|
29
26
|
* @param {mongoose.SchemaOptions} options
|
|
30
27
|
* @returns mongoose.Schema
|
|
31
28
|
*/
|
|
32
|
-
exports.RESERVED_FIELDS = RESERVED_FIELDS;
|
|
33
29
|
function createSchema(definition, options = {}) {
|
|
34
30
|
const schema = new _mongoose.default.Schema(attributesToMongoose(normalizeAttributes({
|
|
35
31
|
...definition.attributes,
|
package/dist/cjs/search.js
CHANGED
|
@@ -16,10 +16,6 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
16
16
|
const {
|
|
17
17
|
ObjectId
|
|
18
18
|
} = _mongoose.default.Types;
|
|
19
|
-
const SORT_SCHEMA = _yada.default.object({
|
|
20
|
-
field: _yada.default.string().required(),
|
|
21
|
-
order: _yada.default.string().allow('desc', 'asc').required()
|
|
22
|
-
}).description('An object describing the sort order of results.');
|
|
23
19
|
function applySearch(schema, definition) {
|
|
24
20
|
validateDefinition(definition);
|
|
25
21
|
schema.static('search', function search(body = {}) {
|
|
@@ -47,7 +43,7 @@ function applySearch(schema, definition) {
|
|
|
47
43
|
Object.assign(query, buildKeywordQuery(keyword, fields));
|
|
48
44
|
}
|
|
49
45
|
Object.assign(query, normalizeQuery(rest, schema.obj));
|
|
50
|
-
const mQuery = this.find(query).sort(resolveSort(sort)).skip(skip).limit(limit);
|
|
46
|
+
const mQuery = this.find(query).sort(resolveSort(sort, schema)).skip(skip).limit(limit);
|
|
51
47
|
|
|
52
48
|
// The following construct is awkward but it allows the mongoose query
|
|
53
49
|
// object to be returned while still ultimately resolving with metadata
|
|
@@ -73,36 +69,56 @@ function applySearch(schema, definition) {
|
|
|
73
69
|
return mQuery;
|
|
74
70
|
});
|
|
75
71
|
}
|
|
76
|
-
function searchValidation(
|
|
77
|
-
|
|
72
|
+
function searchValidation(options = {}) {
|
|
73
|
+
const {
|
|
74
|
+
defaults,
|
|
75
|
+
definition,
|
|
76
|
+
appendSchema
|
|
77
|
+
} = options;
|
|
78
|
+
const searchOptions = {
|
|
78
79
|
..._const.SEARCH_DEFAULTS,
|
|
79
80
|
...(0, _lodash.pick)(definition.search, 'limit', 'sort'),
|
|
80
|
-
...
|
|
81
|
+
...defaults
|
|
81
82
|
};
|
|
82
83
|
const {
|
|
83
84
|
limit,
|
|
84
|
-
sort
|
|
85
|
-
|
|
86
|
-
} = options;
|
|
85
|
+
sort
|
|
86
|
+
} = searchOptions;
|
|
87
87
|
return _yada.default.object({
|
|
88
88
|
ids: _yada.default.array(_validation.OBJECT_ID_SCHEMA),
|
|
89
89
|
keyword: _yada.default.string().description('A keyword to perform a text search against.'),
|
|
90
90
|
skip: _yada.default.number().default(0).description('Number of records to skip.'),
|
|
91
|
-
sort:
|
|
91
|
+
sort: getSortSchema(sort),
|
|
92
92
|
limit: _yada.default.number().positive().default(limit).description('Limits the number of results.'),
|
|
93
|
-
...
|
|
93
|
+
...appendSchema
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
+
function getSortSchema(sort) {
|
|
97
|
+
const schema = _yada.default.object({
|
|
98
|
+
field: _yada.default.string().required(),
|
|
99
|
+
order: _yada.default.string().allow('desc', 'asc').required()
|
|
100
|
+
}).description('An object describing the sort order of results.');
|
|
101
|
+
return _yada.default.allow(schema, _yada.default.array(schema)).default(sort);
|
|
102
|
+
}
|
|
96
103
|
function validateDefinition(definition) {
|
|
97
104
|
if (Array.isArray(definition.search)) {
|
|
98
105
|
(0, _warn.default)(['"search" field on model definition must not be an array.', 'Use "search.fields" to define fields for keyword queries.'].join('\n'));
|
|
99
106
|
throw new Error('Invalid model definition.');
|
|
100
107
|
}
|
|
101
108
|
}
|
|
102
|
-
function resolveSort(sort) {
|
|
103
|
-
if (!
|
|
109
|
+
function resolveSort(sort, schema) {
|
|
110
|
+
if (!sort) {
|
|
111
|
+
sort = [];
|
|
112
|
+
} else if (!Array.isArray(sort)) {
|
|
104
113
|
sort = [sort];
|
|
105
114
|
}
|
|
115
|
+
for (let {
|
|
116
|
+
field
|
|
117
|
+
} of sort) {
|
|
118
|
+
if (!field || !schema.path(field)) {
|
|
119
|
+
throw new Error(`Unknown sort field "${field}".`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
106
122
|
return sort.map(({
|
|
107
123
|
field,
|
|
108
124
|
order
|
package/dist/cjs/validation.js
CHANGED
|
@@ -17,34 +17,35 @@ var _search = require("./search");
|
|
|
17
17
|
var _errors = require("./errors");
|
|
18
18
|
var _softDelete = require("./soft-delete");
|
|
19
19
|
var _utils = require("./utils");
|
|
20
|
-
var _schema = require("./schema");
|
|
21
20
|
var _include = require("./include");
|
|
22
21
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
23
|
-
const
|
|
22
|
+
const DATE_TAGS = {
|
|
24
23
|
'x-schema': 'DateTime',
|
|
25
24
|
'x-description': 'A `string` in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.'
|
|
25
|
+
};
|
|
26
|
+
const OBJECT_ID_SCHEMA = _yada.default.string().mongo().tag({
|
|
27
|
+
'x-schema': 'ObjectId',
|
|
28
|
+
'x-description': 'A 24 character hexadecimal string representing a Mongo [ObjectId](https://bit.ly/3YPtGlU).'
|
|
26
29
|
});
|
|
27
|
-
|
|
30
|
+
exports.OBJECT_ID_SCHEMA = OBJECT_ID_SCHEMA;
|
|
31
|
+
const REFERENCE_SCHEMA = _yada.default.allow(OBJECT_ID_SCHEMA, _yada.default.object({
|
|
32
|
+
id: OBJECT_ID_SCHEMA.required()
|
|
33
|
+
}).custom(obj => {
|
|
34
|
+
return obj.id;
|
|
35
|
+
})).tag({
|
|
36
|
+
'x-schema': 'Reference',
|
|
37
|
+
'x-description': `
|
|
28
38
|
A 24 character hexadecimal string representing a Mongo [ObjectId](https://bit.ly/3YPtGlU).
|
|
29
39
|
An object with an \`id\` field may also be passed, which will be converted into a string.
|
|
30
|
-
|
|
31
|
-
const OBJECT_ID_SCHEMA = _yada.default.custom(async val => {
|
|
32
|
-
const id = String(val.id || val);
|
|
33
|
-
await namedSchemas.objectId.validate(id);
|
|
34
|
-
return id;
|
|
35
|
-
}).tag({
|
|
36
|
-
type: 'ObjectId',
|
|
37
|
-
'x-schema': 'ObjectId',
|
|
38
|
-
'x-description': OBJECT_ID_DESCRIPTION.trim()
|
|
40
|
+
`.trim()
|
|
39
41
|
});
|
|
40
|
-
exports.OBJECT_ID_SCHEMA = OBJECT_ID_SCHEMA;
|
|
41
42
|
const namedSchemas = {
|
|
42
43
|
// Email is special as we are assuming that in
|
|
43
44
|
// all cases lowercase should be allowed but coerced.
|
|
44
45
|
email: _yada.default.string().lowercase().email(),
|
|
45
46
|
// Force "objectId" to have parity with refs.
|
|
46
47
|
// "mongo" is notably excluded here for this reason.
|
|
47
|
-
objectId:
|
|
48
|
+
objectId: OBJECT_ID_SCHEMA,
|
|
48
49
|
ascii: _yada.default.string().ascii(),
|
|
49
50
|
base64: _yada.default.string().base64(),
|
|
50
51
|
btc: _yada.default.string().btc(),
|
|
@@ -76,7 +77,9 @@ function applyValidation(schema, definition) {
|
|
|
76
77
|
model: this,
|
|
77
78
|
appendSchema,
|
|
78
79
|
allowIncludes: true,
|
|
79
|
-
|
|
80
|
+
stripDeleted: true,
|
|
81
|
+
stripTimestamps: true,
|
|
82
|
+
allowExpandedRefs: true,
|
|
80
83
|
requireWriteAccess: true,
|
|
81
84
|
...(hasUnique && {
|
|
82
85
|
assertUniqueOptions: {
|
|
@@ -91,8 +94,10 @@ function applyValidation(schema, definition) {
|
|
|
91
94
|
model: this,
|
|
92
95
|
appendSchema,
|
|
93
96
|
skipRequired: true,
|
|
94
|
-
stripReserved: true,
|
|
95
97
|
stripUnknown: true,
|
|
98
|
+
stripDeleted: true,
|
|
99
|
+
stripTimestamps: true,
|
|
100
|
+
allowExpandedRefs: true,
|
|
96
101
|
requireWriteAccess: true,
|
|
97
102
|
...(hasUnique && {
|
|
98
103
|
assertUniqueOptions: {
|
|
@@ -102,36 +107,57 @@ function applyValidation(schema, definition) {
|
|
|
102
107
|
})
|
|
103
108
|
});
|
|
104
109
|
});
|
|
105
|
-
schema.static('getSearchValidation', function getSearchValidation(
|
|
110
|
+
schema.static('getSearchValidation', function getSearchValidation(options = {}) {
|
|
111
|
+
const {
|
|
112
|
+
defaults,
|
|
113
|
+
includeDeleted,
|
|
114
|
+
...appendSchema
|
|
115
|
+
} = options;
|
|
106
116
|
return getSchemaFromMongoose(schema, {
|
|
117
|
+
model: this,
|
|
107
118
|
allowSearch: true,
|
|
108
119
|
skipRequired: true,
|
|
109
120
|
allowIncludes: true,
|
|
110
121
|
expandDotSyntax: true,
|
|
111
122
|
unwindArrayFields: true,
|
|
112
123
|
requireReadAccess: true,
|
|
113
|
-
|
|
114
|
-
|
|
124
|
+
stripDeleted: !includeDeleted,
|
|
125
|
+
appendSchema: (0, _search.searchValidation)({
|
|
126
|
+
defaults,
|
|
127
|
+
definition,
|
|
128
|
+
appendSchema
|
|
129
|
+
})
|
|
115
130
|
});
|
|
116
131
|
});
|
|
117
132
|
schema.static('getIncludeValidation', function getIncludeValidation() {
|
|
118
133
|
return _include.INCLUDE_FIELD_SCHEMA;
|
|
119
134
|
});
|
|
120
135
|
schema.static('getBaseSchema', function getBaseSchema() {
|
|
121
|
-
return getSchemaFromMongoose(schema
|
|
136
|
+
return getSchemaFromMongoose(schema, {
|
|
137
|
+
stripDeleted: true
|
|
138
|
+
});
|
|
122
139
|
});
|
|
123
140
|
}
|
|
124
141
|
|
|
125
142
|
// Yada schemas
|
|
126
143
|
|
|
127
144
|
function getSchemaFromMongoose(schema, options = {}) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
const fields = getMongooseFields(schema, options);
|
|
146
|
+
return getValidationSchema(fields, options);
|
|
147
|
+
}
|
|
148
|
+
function getMongooseFields(schema, options) {
|
|
149
|
+
const {
|
|
150
|
+
stripTimestamps,
|
|
151
|
+
stripDeleted
|
|
152
|
+
} = options;
|
|
153
|
+
let fields = schema.obj;
|
|
154
|
+
if (stripTimestamps) {
|
|
155
|
+
fields = (0, _lodash.omit)(fields, ['createdAt', 'updatedAt']);
|
|
156
|
+
}
|
|
157
|
+
if (stripDeleted) {
|
|
158
|
+
fields = (0, _lodash.omit)(fields, ['deleted', 'deletedAt']);
|
|
133
159
|
}
|
|
134
|
-
return
|
|
160
|
+
return fields;
|
|
135
161
|
}
|
|
136
162
|
|
|
137
163
|
// Exported for testing
|
|
@@ -191,7 +217,7 @@ function getObjectSchema(arg, options) {
|
|
|
191
217
|
}
|
|
192
218
|
return schema;
|
|
193
219
|
} else {
|
|
194
|
-
return getSchemaForType(arg);
|
|
220
|
+
return getSchemaForType(arg, options);
|
|
195
221
|
}
|
|
196
222
|
}
|
|
197
223
|
function getArraySchema(arr, options) {
|
|
@@ -225,7 +251,7 @@ function getSchemaForTypedef(typedef, options = {}) {
|
|
|
225
251
|
} else if (typeof type === 'object') {
|
|
226
252
|
schema = getObjectSchema(type, options);
|
|
227
253
|
} else {
|
|
228
|
-
schema = getSchemaForType(type);
|
|
254
|
+
schema = getSchemaForType(type, options);
|
|
229
255
|
}
|
|
230
256
|
if (isRequired(typedef, options)) {
|
|
231
257
|
schema = schema.required();
|
|
@@ -258,7 +284,7 @@ function getSchemaForTypedef(typedef, options = {}) {
|
|
|
258
284
|
}
|
|
259
285
|
return schema;
|
|
260
286
|
}
|
|
261
|
-
function getSchemaForType(type) {
|
|
287
|
+
function getSchemaForType(type, options) {
|
|
262
288
|
switch (type) {
|
|
263
289
|
case 'String':
|
|
264
290
|
return _yada.default.string();
|
|
@@ -267,14 +293,18 @@ function getSchemaForType(type) {
|
|
|
267
293
|
case 'Boolean':
|
|
268
294
|
return _yada.default.boolean();
|
|
269
295
|
case 'Date':
|
|
270
|
-
return
|
|
296
|
+
return _yada.default.date().iso().tag(DATE_TAGS);
|
|
271
297
|
case 'Mixed':
|
|
272
298
|
case 'Object':
|
|
273
299
|
return _yada.default.object();
|
|
274
300
|
case 'Array':
|
|
275
301
|
return _yada.default.array();
|
|
276
302
|
case 'ObjectId':
|
|
277
|
-
|
|
303
|
+
if (options.allowExpandedRefs) {
|
|
304
|
+
return REFERENCE_SCHEMA;
|
|
305
|
+
} else {
|
|
306
|
+
return OBJECT_ID_SCHEMA;
|
|
307
|
+
}
|
|
278
308
|
default:
|
|
279
309
|
throw new TypeError(`Unknown schema type ${type}`);
|
|
280
310
|
}
|
|
@@ -293,19 +323,19 @@ function getSearchSchema(schema, type) {
|
|
|
293
323
|
} else if (type === 'Date') {
|
|
294
324
|
return _yada.default.allow(schema, _yada.default.array(schema), _yada.default.object({
|
|
295
325
|
lt: _yada.default.date().iso().tag({
|
|
296
|
-
|
|
326
|
+
...DATE_TAGS,
|
|
297
327
|
description: 'Select dates occurring before.'
|
|
298
328
|
}),
|
|
299
329
|
gt: _yada.default.date().iso().tag({
|
|
300
|
-
|
|
330
|
+
...DATE_TAGS,
|
|
301
331
|
description: 'Select dates occurring after.'
|
|
302
332
|
}),
|
|
303
333
|
lte: _yada.default.date().iso().tag({
|
|
304
|
-
|
|
334
|
+
...DATE_TAGS,
|
|
305
335
|
description: 'Select dates occurring on or before.'
|
|
306
336
|
}),
|
|
307
337
|
gte: _yada.default.date().iso().tag({
|
|
308
|
-
|
|
338
|
+
...DATE_TAGS,
|
|
309
339
|
description: 'Select dates occurring on or after.'
|
|
310
340
|
})
|
|
311
341
|
}).tag({
|
package/package.json
CHANGED
package/src/schema.js
CHANGED
|
@@ -18,13 +18,6 @@ import {
|
|
|
18
18
|
getTupleValidator,
|
|
19
19
|
} from './validation';
|
|
20
20
|
|
|
21
|
-
export const RESERVED_FIELDS = [
|
|
22
|
-
'createdAt',
|
|
23
|
-
'updatedAt',
|
|
24
|
-
'deletedAt',
|
|
25
|
-
'deleted',
|
|
26
|
-
];
|
|
27
|
-
|
|
28
21
|
/**
|
|
29
22
|
* Creates a new Mongoose schema with Bedrock extensions
|
|
30
23
|
* applied. For more about syntax and functionality see
|
package/src/search.js
CHANGED
|
@@ -10,13 +10,6 @@ import warn from './warn';
|
|
|
10
10
|
|
|
11
11
|
const { ObjectId } = mongoose.Types;
|
|
12
12
|
|
|
13
|
-
const SORT_SCHEMA = yd
|
|
14
|
-
.object({
|
|
15
|
-
field: yd.string().required(),
|
|
16
|
-
order: yd.string().allow('desc', 'asc').required(),
|
|
17
|
-
})
|
|
18
|
-
.description('An object describing the sort order of results.');
|
|
19
|
-
|
|
20
13
|
export function applySearch(schema, definition) {
|
|
21
14
|
validateDefinition(definition);
|
|
22
15
|
|
|
@@ -42,7 +35,7 @@ export function applySearch(schema, definition) {
|
|
|
42
35
|
Object.assign(query, normalizeQuery(rest, schema.obj));
|
|
43
36
|
|
|
44
37
|
const mQuery = this.find(query)
|
|
45
|
-
.sort(resolveSort(sort))
|
|
38
|
+
.sort(resolveSort(sort, schema))
|
|
46
39
|
.skip(skip)
|
|
47
40
|
.limit(limit);
|
|
48
41
|
|
|
@@ -76,14 +69,16 @@ export function applySearch(schema, definition) {
|
|
|
76
69
|
});
|
|
77
70
|
}
|
|
78
71
|
|
|
79
|
-
export function searchValidation(
|
|
80
|
-
|
|
72
|
+
export function searchValidation(options = {}) {
|
|
73
|
+
const { defaults, definition, appendSchema } = options;
|
|
74
|
+
|
|
75
|
+
const searchOptions = {
|
|
81
76
|
...SEARCH_DEFAULTS,
|
|
82
77
|
...pick(definition.search, 'limit', 'sort'),
|
|
83
|
-
...
|
|
78
|
+
...defaults,
|
|
84
79
|
};
|
|
85
80
|
|
|
86
|
-
const { limit, sort
|
|
81
|
+
const { limit, sort } = searchOptions;
|
|
87
82
|
|
|
88
83
|
return yd.object({
|
|
89
84
|
ids: yd.array(OBJECT_ID_SCHEMA),
|
|
@@ -91,16 +86,26 @@ export function searchValidation(definition, options = {}) {
|
|
|
91
86
|
.string()
|
|
92
87
|
.description('A keyword to perform a text search against.'),
|
|
93
88
|
skip: yd.number().default(0).description('Number of records to skip.'),
|
|
94
|
-
sort:
|
|
89
|
+
sort: getSortSchema(sort),
|
|
95
90
|
limit: yd
|
|
96
91
|
.number()
|
|
97
92
|
.positive()
|
|
98
93
|
.default(limit)
|
|
99
94
|
.description('Limits the number of results.'),
|
|
100
|
-
...
|
|
95
|
+
...appendSchema,
|
|
101
96
|
});
|
|
102
97
|
}
|
|
103
98
|
|
|
99
|
+
function getSortSchema(sort) {
|
|
100
|
+
const schema = yd
|
|
101
|
+
.object({
|
|
102
|
+
field: yd.string().required(),
|
|
103
|
+
order: yd.string().allow('desc', 'asc').required(),
|
|
104
|
+
})
|
|
105
|
+
.description('An object describing the sort order of results.');
|
|
106
|
+
return yd.allow(schema, yd.array(schema)).default(sort);
|
|
107
|
+
}
|
|
108
|
+
|
|
104
109
|
function validateDefinition(definition) {
|
|
105
110
|
if (Array.isArray(definition.search)) {
|
|
106
111
|
warn(
|
|
@@ -113,10 +118,17 @@ function validateDefinition(definition) {
|
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
|
|
116
|
-
function resolveSort(sort) {
|
|
117
|
-
if (!
|
|
121
|
+
function resolveSort(sort, schema) {
|
|
122
|
+
if (!sort) {
|
|
123
|
+
sort = [];
|
|
124
|
+
} else if (!Array.isArray(sort)) {
|
|
118
125
|
sort = [sort];
|
|
119
126
|
}
|
|
127
|
+
for (let { field } of sort) {
|
|
128
|
+
if (!field || !schema.path(field)) {
|
|
129
|
+
throw new Error(`Unknown sort field "${field}".`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
120
132
|
return sort.map(({ field, order }) => {
|
|
121
133
|
return [field, order === 'desc' ? -1 : 1];
|
|
122
134
|
});
|
package/src/validation.js
CHANGED
|
@@ -8,30 +8,37 @@ import { searchValidation } from './search';
|
|
|
8
8
|
import { PermissionsError, ImplementationError } from './errors';
|
|
9
9
|
import { hasUniqueConstraints, assertUnique } from './soft-delete';
|
|
10
10
|
import { isMongooseSchema, isSchemaTypedef } from './utils';
|
|
11
|
-
import { RESERVED_FIELDS } from './schema';
|
|
12
11
|
import { INCLUDE_FIELD_SCHEMA } from './include';
|
|
13
12
|
|
|
14
|
-
const
|
|
13
|
+
const DATE_TAGS = {
|
|
15
14
|
'x-schema': 'DateTime',
|
|
16
15
|
'x-description':
|
|
17
16
|
'A `string` in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format.',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const OBJECT_ID_SCHEMA = yd.string().mongo().tag({
|
|
20
|
+
'x-schema': 'ObjectId',
|
|
21
|
+
'x-description':
|
|
22
|
+
'A 24 character hexadecimal string representing a Mongo [ObjectId](https://bit.ly/3YPtGlU).',
|
|
18
23
|
});
|
|
19
24
|
|
|
20
|
-
const
|
|
25
|
+
const REFERENCE_SCHEMA = yd
|
|
26
|
+
.allow(
|
|
27
|
+
OBJECT_ID_SCHEMA,
|
|
28
|
+
yd
|
|
29
|
+
.object({
|
|
30
|
+
id: OBJECT_ID_SCHEMA.required(),
|
|
31
|
+
})
|
|
32
|
+
.custom((obj) => {
|
|
33
|
+
return obj.id;
|
|
34
|
+
})
|
|
35
|
+
)
|
|
36
|
+
.tag({
|
|
37
|
+
'x-schema': 'Reference',
|
|
38
|
+
'x-description': `
|
|
21
39
|
A 24 character hexadecimal string representing a Mongo [ObjectId](https://bit.ly/3YPtGlU).
|
|
22
40
|
An object with an \`id\` field may also be passed, which will be converted into a string.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
export const OBJECT_ID_SCHEMA = yd
|
|
26
|
-
.custom(async (val) => {
|
|
27
|
-
const id = String(val.id || val);
|
|
28
|
-
await namedSchemas.objectId.validate(id);
|
|
29
|
-
return id;
|
|
30
|
-
})
|
|
31
|
-
.tag({
|
|
32
|
-
type: 'ObjectId',
|
|
33
|
-
'x-schema': 'ObjectId',
|
|
34
|
-
'x-description': OBJECT_ID_DESCRIPTION.trim(),
|
|
41
|
+
`.trim(),
|
|
35
42
|
});
|
|
36
43
|
|
|
37
44
|
const namedSchemas = {
|
|
@@ -40,7 +47,7 @@ const namedSchemas = {
|
|
|
40
47
|
email: yd.string().lowercase().email(),
|
|
41
48
|
// Force "objectId" to have parity with refs.
|
|
42
49
|
// "mongo" is notably excluded here for this reason.
|
|
43
|
-
objectId:
|
|
50
|
+
objectId: OBJECT_ID_SCHEMA,
|
|
44
51
|
|
|
45
52
|
ascii: yd.string().ascii(),
|
|
46
53
|
base64: yd.string().base64(),
|
|
@@ -78,7 +85,9 @@ export function applyValidation(schema, definition) {
|
|
|
78
85
|
model: this,
|
|
79
86
|
appendSchema,
|
|
80
87
|
allowIncludes: true,
|
|
81
|
-
|
|
88
|
+
stripDeleted: true,
|
|
89
|
+
stripTimestamps: true,
|
|
90
|
+
allowExpandedRefs: true,
|
|
82
91
|
requireWriteAccess: true,
|
|
83
92
|
...(hasUnique && {
|
|
84
93
|
assertUniqueOptions: {
|
|
@@ -97,8 +106,10 @@ export function applyValidation(schema, definition) {
|
|
|
97
106
|
model: this,
|
|
98
107
|
appendSchema,
|
|
99
108
|
skipRequired: true,
|
|
100
|
-
stripReserved: true,
|
|
101
109
|
stripUnknown: true,
|
|
110
|
+
stripDeleted: true,
|
|
111
|
+
stripTimestamps: true,
|
|
112
|
+
allowExpandedRefs: true,
|
|
102
113
|
requireWriteAccess: true,
|
|
103
114
|
...(hasUnique && {
|
|
104
115
|
assertUniqueOptions: {
|
|
@@ -112,16 +123,23 @@ export function applyValidation(schema, definition) {
|
|
|
112
123
|
|
|
113
124
|
schema.static(
|
|
114
125
|
'getSearchValidation',
|
|
115
|
-
function getSearchValidation(
|
|
126
|
+
function getSearchValidation(options = {}) {
|
|
127
|
+
const { defaults, includeDeleted, ...appendSchema } = options;
|
|
128
|
+
|
|
116
129
|
return getSchemaFromMongoose(schema, {
|
|
130
|
+
model: this,
|
|
117
131
|
allowSearch: true,
|
|
118
132
|
skipRequired: true,
|
|
119
133
|
allowIncludes: true,
|
|
120
134
|
expandDotSyntax: true,
|
|
121
135
|
unwindArrayFields: true,
|
|
122
136
|
requireReadAccess: true,
|
|
123
|
-
|
|
124
|
-
|
|
137
|
+
stripDeleted: !includeDeleted,
|
|
138
|
+
appendSchema: searchValidation({
|
|
139
|
+
defaults,
|
|
140
|
+
definition,
|
|
141
|
+
appendSchema,
|
|
142
|
+
}),
|
|
125
143
|
});
|
|
126
144
|
}
|
|
127
145
|
);
|
|
@@ -131,18 +149,29 @@ export function applyValidation(schema, definition) {
|
|
|
131
149
|
});
|
|
132
150
|
|
|
133
151
|
schema.static('getBaseSchema', function getBaseSchema() {
|
|
134
|
-
return getSchemaFromMongoose(schema
|
|
152
|
+
return getSchemaFromMongoose(schema, {
|
|
153
|
+
stripDeleted: true,
|
|
154
|
+
});
|
|
135
155
|
});
|
|
136
156
|
}
|
|
137
157
|
|
|
138
158
|
// Yada schemas
|
|
139
159
|
|
|
140
160
|
function getSchemaFromMongoose(schema, options = {}) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
161
|
+
const fields = getMongooseFields(schema, options);
|
|
162
|
+
return getValidationSchema(fields, options);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function getMongooseFields(schema, options) {
|
|
166
|
+
const { stripTimestamps, stripDeleted } = options;
|
|
167
|
+
let fields = schema.obj;
|
|
168
|
+
if (stripTimestamps) {
|
|
169
|
+
fields = omit(fields, ['createdAt', 'updatedAt']);
|
|
144
170
|
}
|
|
145
|
-
|
|
171
|
+
if (stripDeleted) {
|
|
172
|
+
fields = omit(fields, ['deleted', 'deletedAt']);
|
|
173
|
+
}
|
|
174
|
+
return fields;
|
|
146
175
|
}
|
|
147
176
|
|
|
148
177
|
// Exported for testing
|
|
@@ -197,7 +226,7 @@ function getObjectSchema(arg, options) {
|
|
|
197
226
|
|
|
198
227
|
return schema;
|
|
199
228
|
} else {
|
|
200
|
-
return getSchemaForType(arg);
|
|
229
|
+
return getSchemaForType(arg, options);
|
|
201
230
|
}
|
|
202
231
|
}
|
|
203
232
|
|
|
@@ -233,7 +262,7 @@ function getSchemaForTypedef(typedef, options = {}) {
|
|
|
233
262
|
} else if (typeof type === 'object') {
|
|
234
263
|
schema = getObjectSchema(type, options);
|
|
235
264
|
} else {
|
|
236
|
-
schema = getSchemaForType(type);
|
|
265
|
+
schema = getSchemaForType(type, options);
|
|
237
266
|
}
|
|
238
267
|
|
|
239
268
|
if (isRequired(typedef, options)) {
|
|
@@ -269,7 +298,7 @@ function getSchemaForTypedef(typedef, options = {}) {
|
|
|
269
298
|
return schema;
|
|
270
299
|
}
|
|
271
300
|
|
|
272
|
-
function getSchemaForType(type) {
|
|
301
|
+
function getSchemaForType(type, options) {
|
|
273
302
|
switch (type) {
|
|
274
303
|
case 'String':
|
|
275
304
|
return yd.string();
|
|
@@ -278,14 +307,18 @@ function getSchemaForType(type) {
|
|
|
278
307
|
case 'Boolean':
|
|
279
308
|
return yd.boolean();
|
|
280
309
|
case 'Date':
|
|
281
|
-
return
|
|
310
|
+
return yd.date().iso().tag(DATE_TAGS);
|
|
282
311
|
case 'Mixed':
|
|
283
312
|
case 'Object':
|
|
284
313
|
return yd.object();
|
|
285
314
|
case 'Array':
|
|
286
315
|
return yd.array();
|
|
287
316
|
case 'ObjectId':
|
|
288
|
-
|
|
317
|
+
if (options.allowExpandedRefs) {
|
|
318
|
+
return REFERENCE_SCHEMA;
|
|
319
|
+
} else {
|
|
320
|
+
return OBJECT_ID_SCHEMA;
|
|
321
|
+
}
|
|
289
322
|
default:
|
|
290
323
|
throw new TypeError(`Unknown schema type ${type}`);
|
|
291
324
|
}
|
|
@@ -322,22 +355,34 @@ function getSearchSchema(schema, type) {
|
|
|
322
355
|
yd.array(schema),
|
|
323
356
|
yd
|
|
324
357
|
.object({
|
|
325
|
-
lt: yd
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
358
|
+
lt: yd
|
|
359
|
+
.date()
|
|
360
|
+
.iso()
|
|
361
|
+
.tag({
|
|
362
|
+
...DATE_TAGS,
|
|
363
|
+
description: 'Select dates occurring before.',
|
|
364
|
+
}),
|
|
365
|
+
gt: yd
|
|
366
|
+
.date()
|
|
367
|
+
.iso()
|
|
368
|
+
.tag({
|
|
369
|
+
...DATE_TAGS,
|
|
370
|
+
description: 'Select dates occurring after.',
|
|
371
|
+
}),
|
|
372
|
+
lte: yd
|
|
373
|
+
.date()
|
|
374
|
+
.iso()
|
|
375
|
+
.tag({
|
|
376
|
+
...DATE_TAGS,
|
|
377
|
+
description: 'Select dates occurring on or before.',
|
|
378
|
+
}),
|
|
379
|
+
gte: yd
|
|
380
|
+
.date()
|
|
381
|
+
.iso()
|
|
382
|
+
.tag({
|
|
383
|
+
...DATE_TAGS,
|
|
384
|
+
description: 'Select dates occurring on or after.',
|
|
385
|
+
}),
|
|
341
386
|
})
|
|
342
387
|
.tag({
|
|
343
388
|
'x-schema': 'DateRange',
|
package/types/schema.d.ts
CHANGED
|
@@ -66,6 +66,5 @@ export function createSchema(definition: object, options?: mongoose.SchemaOption
|
|
|
66
66
|
overwriteModels?: boolean;
|
|
67
67
|
}, any>;
|
|
68
68
|
export function normalizeAttributes(arg: any, path?: any[]): any;
|
|
69
|
-
export const RESERVED_FIELDS: string[];
|
|
70
69
|
import mongoose from "mongoose";
|
|
71
70
|
//# sourceMappingURL=schema.d.ts.map
|