@lykmapipo/mongoose-common 0.39.0 → 0.40.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/.babelrc +12 -0
- package/.editorconfig +14 -0
- package/.eslintignore +1 -0
- package/.eslintrc +11 -0
- package/.prettierignore +0 -1
- package/CHANGELOG.md +16 -0
- package/LICENSE +2 -2
- package/README.md +22 -8
- package/SECURITY.md +3 -0
- package/es/index.js +1151 -0
- package/lib/index.js +1290 -0
- package/package.json +101 -38
- package/rollup.config.js +26 -0
- package/{lib → src}/.gitkeep +0 -0
- package/{index.js → src/index.js} +209 -226
- package/src/plugins/.gitkeep +0 -0
- package/src/plugins/error.plugin.js +183 -0
- package/src/plugins/path.plugin.js +39 -0
- package/{lib → src/plugins}/seed.plugin.js +120 -70
- package/.jsbeautifyrc +0 -7
- package/lib/error.plugin.js +0 -163
- package/lib/path.plugin.js +0 -43
package/lib/index.js
ADDED
@@ -0,0 +1,1290 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const lodash = require('lodash');
|
4
|
+
const crypto = require('crypto');
|
5
|
+
const common = require('@lykmapipo/common');
|
6
|
+
const mongoose = require('mongoose-valid8');
|
7
|
+
const utils = require('mongoose/lib/utils');
|
8
|
+
const mongooseConnection = require('@lykmapipo/mongoose-connection');
|
9
|
+
const path = require('path');
|
10
|
+
const async = require('async');
|
11
|
+
const env = require('@lykmapipo/env');
|
12
|
+
|
13
|
+
/**
|
14
|
+
* @function isUniqueError
|
15
|
+
* @name isUniqueError
|
16
|
+
* @description Check if the given error is a unique mongodb error.
|
17
|
+
* @param {object} error valid error object to test.
|
18
|
+
* @returns {boolean} true if and only if it is an unique error.
|
19
|
+
* @version 0.2.0
|
20
|
+
* @since 0.1.0
|
21
|
+
* @private
|
22
|
+
*/
|
23
|
+
const isUniqueError = (error) => {
|
24
|
+
return (
|
25
|
+
error &&
|
26
|
+
(error.name === 'BulkWriteError' ||
|
27
|
+
error.name === 'MongoError' ||
|
28
|
+
error.name === 'MongoServerError' ||
|
29
|
+
error.name === 'MongoBulkWriteError') &&
|
30
|
+
(error.code === 11000 || error.code === 11001) &&
|
31
|
+
!lodash.isEmpty(error.message)
|
32
|
+
);
|
33
|
+
};
|
34
|
+
|
35
|
+
/**
|
36
|
+
* @function parseErrorPaths
|
37
|
+
* @name parseErrorPaths
|
38
|
+
* @description Parse paths found in unique mongodb error.
|
39
|
+
* @param {object} schema valid mongoose schema.
|
40
|
+
* @param {Error} error valid mongodb error.
|
41
|
+
* @returns {string[]} found paths in error.
|
42
|
+
* @version 0.19.0
|
43
|
+
* @since 0.1.0
|
44
|
+
* @private
|
45
|
+
*/
|
46
|
+
const parseErrorPaths = (schema, error) => {
|
47
|
+
// back off if no error message
|
48
|
+
if (!error.message) {
|
49
|
+
return [];
|
50
|
+
}
|
51
|
+
|
52
|
+
// obtain paths from error message
|
53
|
+
let paths = lodash.nth(error.message.match(/index: (.+?) dup key:/), 1) || '';
|
54
|
+
paths = paths.split('$').pop();
|
55
|
+
|
56
|
+
// handle compound unique paths index
|
57
|
+
paths = [].concat(paths.split('_'));
|
58
|
+
|
59
|
+
// in case for id ensure _id too and compact paths
|
60
|
+
paths = common.uniq([
|
61
|
+
...paths,
|
62
|
+
...lodash.map(paths, (pathName) => (pathName === 'id' ? '_id' : pathName)),
|
63
|
+
]);
|
64
|
+
|
65
|
+
// ensure paths are within schema
|
66
|
+
paths = lodash.filter(paths, (pathName) => !lodash.isEmpty(schema.path(pathName)));
|
67
|
+
|
68
|
+
// return found paths
|
69
|
+
return paths;
|
70
|
+
};
|
71
|
+
|
72
|
+
/**
|
73
|
+
* @function parseErrorValues
|
74
|
+
* @name parseErrorValues
|
75
|
+
* @description Parse paths value found in unique mongodb error.
|
76
|
+
* @param {string[]} paths paths found in unique mongodb error.
|
77
|
+
* @param {Error} error valid mongodb error.
|
78
|
+
* @returns {string[]} found paths value from error.
|
79
|
+
* @version 0.19.0
|
80
|
+
* @since 0.1.0
|
81
|
+
* @private
|
82
|
+
*/
|
83
|
+
const parseErrorValues = (paths, error) => {
|
84
|
+
// back off if no error message
|
85
|
+
if (!error.message) {
|
86
|
+
return [];
|
87
|
+
}
|
88
|
+
|
89
|
+
// obtain paths value
|
90
|
+
let values = lodash.nth(error.message.match(/dup key: { (.+?) }/), 1) || '';
|
91
|
+
values = common.uniq(
|
92
|
+
[].concat(values.match(/'(.+?)'/g)).concat(values.match(/"(.+?)"/g))
|
93
|
+
); // enclosed with quotes
|
94
|
+
values = lodash.map(values, (v) => v.replace(/^"(.+?)"$/, '$1')); // double quotes
|
95
|
+
values = lodash.map(values, (v) => v.replace(/^'(.+?)'$/, '$1')); // single quotes
|
96
|
+
values = paths.length === 1 ? [values.join(' ')] : values;
|
97
|
+
|
98
|
+
// return parsed paths values
|
99
|
+
return values;
|
100
|
+
};
|
101
|
+
|
102
|
+
/**
|
103
|
+
* @function uniqueErrorPlugin
|
104
|
+
* @name uniqueErrorPlugin
|
105
|
+
* @description Plugin to handle mongodb unique error
|
106
|
+
* @param {object} schema valid mongoose schema
|
107
|
+
* @version 0.2.0
|
108
|
+
* @since 0.1.0
|
109
|
+
* @public
|
110
|
+
*/
|
111
|
+
function uniqueErrorPlugin(schema) {
|
112
|
+
/**
|
113
|
+
* @function handleUniqueError
|
114
|
+
* @name handleUniqueError
|
115
|
+
* @description Handle mongodb unique error and transform to mongoose error
|
116
|
+
* @param {Error|object} error valid mongodb unique error
|
117
|
+
* @param {object} doc valid mongoose document
|
118
|
+
* @param {Function} next callback to invoke on success or error
|
119
|
+
* @returns {Error} valid mongoose error
|
120
|
+
* @version 0.2.0
|
121
|
+
* @since 0.1.0
|
122
|
+
* @private
|
123
|
+
*/
|
124
|
+
function handleUniqueError(error, doc, next) {
|
125
|
+
// this: Model instance context
|
126
|
+
|
127
|
+
// obtain current instance
|
128
|
+
const instance = doc || this;
|
129
|
+
|
130
|
+
// continue if is not unique error
|
131
|
+
if (!isUniqueError(error)) {
|
132
|
+
return next(error);
|
133
|
+
}
|
134
|
+
|
135
|
+
// obtain index name
|
136
|
+
const indexName =
|
137
|
+
lodash.nth(error.message.match(/index: (.+?) dup key:/), 1) || '';
|
138
|
+
|
139
|
+
// obtain unique paths from error
|
140
|
+
const paths = parseErrorPaths(schema, error);
|
141
|
+
|
142
|
+
// obtain paths value from error
|
143
|
+
const values = parseErrorValues(paths, error);
|
144
|
+
|
145
|
+
// build mongoose validations error bag
|
146
|
+
if (!lodash.isEmpty(paths) && !lodash.isEmpty(values)) {
|
147
|
+
const errors = {};
|
148
|
+
|
149
|
+
lodash.forEach(paths, (pathName, index) => {
|
150
|
+
// construct path error properties
|
151
|
+
let pathValue = lodash.nth(values, index);
|
152
|
+
if (lodash.isFunction(instance.get)) {
|
153
|
+
pathValue = instance.get(pathName);
|
154
|
+
}
|
155
|
+
const props = {
|
156
|
+
type: 'unique',
|
157
|
+
path: pathName,
|
158
|
+
value: pathValue,
|
159
|
+
message: 'Path `{PATH}` ({VALUE}) is not unique.',
|
160
|
+
reason: error.message,
|
161
|
+
index: indexName,
|
162
|
+
};
|
163
|
+
|
164
|
+
// construct path validation error
|
165
|
+
const pathError = new mongoose.Error.ValidatorError(props);
|
166
|
+
pathError.index = indexName;
|
167
|
+
errors[pathName] = pathError;
|
168
|
+
});
|
169
|
+
|
170
|
+
// build mongoose validation error
|
171
|
+
const err = new mongoose.Error.ValidationError();
|
172
|
+
err.status = err.status || 400;
|
173
|
+
err.errors = errors;
|
174
|
+
|
175
|
+
return next(err);
|
176
|
+
}
|
177
|
+
|
178
|
+
// continue with error
|
179
|
+
return next(error);
|
180
|
+
}
|
181
|
+
|
182
|
+
// plugin unique error handler
|
183
|
+
schema.post('save', handleUniqueError);
|
184
|
+
schema.post('insertMany', handleUniqueError);
|
185
|
+
schema.post('findOneAndReplace', handleUniqueError);
|
186
|
+
schema.post('findOneAndUpdate', handleUniqueError);
|
187
|
+
schema.post('replaceOne', handleUniqueError);
|
188
|
+
schema.post('update', handleUniqueError);
|
189
|
+
schema.post('updateMany', handleUniqueError);
|
190
|
+
schema.post('updateOne', handleUniqueError);
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* @function path
|
195
|
+
* @name path
|
196
|
+
* @description obtain schema path from model
|
197
|
+
* @param {object} schema valid mongoose schema instance
|
198
|
+
* @version 0.1.0
|
199
|
+
* @since 0.1.0
|
200
|
+
* @public
|
201
|
+
*/
|
202
|
+
const pathPlugin = (schema) => {
|
203
|
+
// register path
|
204
|
+
const canNotGetPath = !lodash.isFunction(schema.statics.path);
|
205
|
+
|
206
|
+
if (canNotGetPath) {
|
207
|
+
// eslint-disable-next-line no-param-reassign
|
208
|
+
schema.statics.path = function path(pathName) {
|
209
|
+
// initalize path
|
210
|
+
let $path;
|
211
|
+
|
212
|
+
// tokenize path
|
213
|
+
const paths = lodash.split(pathName, '.');
|
214
|
+
|
215
|
+
// iterate on schema recursive to get path schema
|
216
|
+
lodash.forEach(paths, function getPath(part) {
|
217
|
+
// obtain schema to resolve path
|
218
|
+
const $schema = $path ? $path.schema : schema;
|
219
|
+
$path = $schema && $schema.path ? $schema.path(part) : undefined;
|
220
|
+
});
|
221
|
+
|
222
|
+
// fall back to direct path
|
223
|
+
$path = $path || schema.path(pathName);
|
224
|
+
|
225
|
+
// return found path
|
226
|
+
return $path;
|
227
|
+
};
|
228
|
+
}
|
229
|
+
};
|
230
|
+
|
231
|
+
/**
|
232
|
+
* @name loadPathSeeds
|
233
|
+
* @description load seeds from paths
|
234
|
+
* @param {string} collectionName valid collection name
|
235
|
+
* @returns {object|object[]} given collection seed from a path
|
236
|
+
* @since 0.21.0
|
237
|
+
* @version 0.2.0
|
238
|
+
* @private
|
239
|
+
*/
|
240
|
+
function loadPathSeeds(collectionName) {
|
241
|
+
// resolve seed path
|
242
|
+
const BASE_PATH = env.getString('BASE_PATH', process.cwd());
|
243
|
+
let SEED_PATH = env.getString('SEED_PATH', path.join(BASE_PATH, 'seeds'));
|
244
|
+
SEED_PATH = path.resolve(SEED_PATH, collectionName);
|
245
|
+
|
246
|
+
// try load seeds from path
|
247
|
+
try {
|
248
|
+
// eslint-disable-next-line import/no-dynamic-require, global-require
|
249
|
+
let seeds = require(SEED_PATH);
|
250
|
+
// honor es6 default exports
|
251
|
+
seeds = [].concat(lodash.isArray(seeds.default) ? seeds.default : seeds);
|
252
|
+
return seeds;
|
253
|
+
} catch (e) {
|
254
|
+
return [];
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
/**
|
259
|
+
* @function clearAndSeedModel
|
260
|
+
* @name clearAndSeedModel
|
261
|
+
* @description clear and seed the given model data
|
262
|
+
* @param {object|object[]|Function} data valid model data
|
263
|
+
* @param {Function} done callback to invoke on success or error
|
264
|
+
* @returns {object} seed results
|
265
|
+
* @since 0.21.0
|
266
|
+
* @version 0.2.0
|
267
|
+
* @private
|
268
|
+
*/
|
269
|
+
function seedModel(data, done) {
|
270
|
+
// this: Model static context
|
271
|
+
|
272
|
+
// normalize arguments
|
273
|
+
let seeds = [];
|
274
|
+
let cb = lodash.noop;
|
275
|
+
let filterFn = (val) => val;
|
276
|
+
let transformFn = (val) => val;
|
277
|
+
if (lodash.isFunction(data)) {
|
278
|
+
cb = data;
|
279
|
+
}
|
280
|
+
if (lodash.isArray(data)) {
|
281
|
+
seeds = [].concat(data);
|
282
|
+
}
|
283
|
+
if (lodash.isPlainObject(data)) {
|
284
|
+
filterFn = data.filter || filterFn;
|
285
|
+
transformFn = data.transform || transformFn;
|
286
|
+
seeds = data.data || lodash.omit(data, 'filter', 'transform');
|
287
|
+
seeds = lodash.isArray(seeds) ? seeds : common.mergeObjects(seeds);
|
288
|
+
seeds = [].concat(seeds);
|
289
|
+
seeds = lodash.filter(seeds, (seed) => !lodash.isEmpty(seed));
|
290
|
+
}
|
291
|
+
if (lodash.isFunction(done)) {
|
292
|
+
cb = done || cb;
|
293
|
+
}
|
294
|
+
// let seeds = _.isFunction(data) ? [] : [].concat(data);
|
295
|
+
// const cb = _.isFunction(data) ? data : done;
|
296
|
+
|
297
|
+
// compact seeds
|
298
|
+
const collectionName =
|
299
|
+
lodash.get(this, 'collection.name') || lodash.get(this, 'collection.collectionName');
|
300
|
+
|
301
|
+
// ignore path seeds if seed provided
|
302
|
+
if (lodash.isEmpty(seeds)) {
|
303
|
+
const pathSeeds = loadPathSeeds(collectionName);
|
304
|
+
seeds = lodash.compact([...seeds, ...pathSeeds]);
|
305
|
+
seeds = lodash.filter(seeds, (seed) => !lodash.isEmpty(seed));
|
306
|
+
}
|
307
|
+
|
308
|
+
// filter seeds
|
309
|
+
seeds = lodash.filter(seeds, filterFn);
|
310
|
+
|
311
|
+
// transform seeds
|
312
|
+
seeds = lodash.map(seeds, transformFn);
|
313
|
+
|
314
|
+
// filter empty seeds
|
315
|
+
seeds = lodash.filter(seeds, (seed) => !lodash.isEmpty(seed));
|
316
|
+
|
317
|
+
// find existing instance fullfill seed criteria
|
318
|
+
const findExisting = (seed, afterFind) => {
|
319
|
+
// map seed to criteria
|
320
|
+
const canProvideCriteria = lodash.isFunction(this.prepareSeedCriteria);
|
321
|
+
let prepareSeedCriteria = ($seed) => $seed;
|
322
|
+
if (canProvideCriteria) {
|
323
|
+
prepareSeedCriteria = this.prepareSeedCriteria;
|
324
|
+
}
|
325
|
+
let criteria = prepareSeedCriteria(seed);
|
326
|
+
criteria = lodash.omit(criteria, 'populate');
|
327
|
+
|
328
|
+
// find existing data
|
329
|
+
return this.findOne(criteria, afterFind);
|
330
|
+
};
|
331
|
+
|
332
|
+
// fetch existing dependency
|
333
|
+
const fetchDependency = (dependency, afterDependency) => {
|
334
|
+
// obtain options
|
335
|
+
const { model, match, select, array } = dependency;
|
336
|
+
|
337
|
+
const afterFetchDependency = (error, found) => {
|
338
|
+
const result = lodash.isEmpty(found) ? undefined : found;
|
339
|
+
return afterDependency(error, result);
|
340
|
+
};
|
341
|
+
|
342
|
+
// try fetch with provide options
|
343
|
+
if (lodash.isString(model) && lodash.isPlainObject(match)) {
|
344
|
+
try {
|
345
|
+
const Model = mongoose.model(model);
|
346
|
+
if (array) {
|
347
|
+
return Model.find(
|
348
|
+
match,
|
349
|
+
common.mergeObjects(select, { _id: 1 }),
|
350
|
+
{ autopopulate: false },
|
351
|
+
afterFetchDependency
|
352
|
+
);
|
353
|
+
}
|
354
|
+
return Model.findOne(
|
355
|
+
match,
|
356
|
+
common.mergeObjects(select, { _id: 1 }),
|
357
|
+
{ autopopulate: false },
|
358
|
+
afterFetchDependency
|
359
|
+
);
|
360
|
+
} catch (e) {
|
361
|
+
return afterDependency(e);
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
// backoff: invalid options
|
366
|
+
return afterDependency(new Error('Invalid Populate Options'));
|
367
|
+
};
|
368
|
+
|
369
|
+
// fetch dependencies exclude ignored
|
370
|
+
const fetchDependencyExcludeIgnore = (dependency, afterDependency) => {
|
371
|
+
// obtain options
|
372
|
+
const { ignore = {} } = dependency;
|
373
|
+
return async.waterfall(
|
374
|
+
[
|
375
|
+
(next) => {
|
376
|
+
if (lodash.isEmpty(ignore)) {
|
377
|
+
return next(null, []);
|
378
|
+
}
|
379
|
+
const ignoreCriteria = lodash.omit(ignore, 'select');
|
380
|
+
return fetchDependency(ignoreCriteria, next);
|
381
|
+
}, // fetch ignored
|
382
|
+
(ignored, next) => {
|
383
|
+
// use ignored
|
384
|
+
const ignorePath = ignore.path || '_id';
|
385
|
+
const ignoredIds = lodash.compact(
|
386
|
+
lodash.map([].concat(ignored), (val) => common.idOf(val))
|
387
|
+
);
|
388
|
+
const { model, select, array } = common.mergeObjects(dependency);
|
389
|
+
let { match } = common.mergeObjects(dependency);
|
390
|
+
if (!lodash.isEmpty(ignoredIds)) {
|
391
|
+
match = common.mergeObjects(
|
392
|
+
{
|
393
|
+
[ignorePath]: { $nin: ignoredIds },
|
394
|
+
},
|
395
|
+
match
|
396
|
+
);
|
397
|
+
}
|
398
|
+
return fetchDependency({ model, match, select, array }, next);
|
399
|
+
}, // fetch dependencies exclude ignored
|
400
|
+
],
|
401
|
+
afterDependency
|
402
|
+
);
|
403
|
+
};
|
404
|
+
|
405
|
+
// fetch existing seed dependencies
|
406
|
+
// TODO: optimize queries
|
407
|
+
const fetchDependencies = (seed, afterDependencies) => {
|
408
|
+
let dependencies = common.mergeObjects(seed.populate);
|
409
|
+
if (lodash.isPlainObject(dependencies) && !lodash.isEmpty(dependencies)) {
|
410
|
+
dependencies = lodash.mapValues(dependencies, (dependency) => {
|
411
|
+
return (afterDependency) => {
|
412
|
+
return fetchDependencyExcludeIgnore(dependency, afterDependency);
|
413
|
+
};
|
414
|
+
});
|
415
|
+
return async.parallel(dependencies, afterDependencies);
|
416
|
+
}
|
417
|
+
return afterDependencies(null, seed);
|
418
|
+
};
|
419
|
+
|
420
|
+
// merge existing with seed data
|
421
|
+
const mergeOne = (found, $data, afterMergeOne) => {
|
422
|
+
if (found) {
|
423
|
+
const SEED_FRESH = env.getBoolean('SEED_FRESH', false);
|
424
|
+
let updates = {};
|
425
|
+
if (SEED_FRESH) {
|
426
|
+
updates = common.mergeObjects(found.toObject(), $data);
|
427
|
+
} else {
|
428
|
+
updates = common.mergeObjects($data, found.toObject());
|
429
|
+
}
|
430
|
+
found.set(updates);
|
431
|
+
// eslint-disable-next-line no-param-reassign
|
432
|
+
found.updatedAt = new Date();
|
433
|
+
} else {
|
434
|
+
// eslint-disable-next-line no-param-reassign
|
435
|
+
found = new this($data);
|
436
|
+
}
|
437
|
+
return found.put ? found.put(afterMergeOne) : found.save(afterMergeOne);
|
438
|
+
};
|
439
|
+
|
440
|
+
// update or create seed
|
441
|
+
const upsertOne = (seed, afterUpsert) => {
|
442
|
+
return async.waterfall(
|
443
|
+
[
|
444
|
+
(next) => {
|
445
|
+
fetchDependencies(seed, (error, dependencies) => {
|
446
|
+
if (error) {
|
447
|
+
return next(error);
|
448
|
+
}
|
449
|
+
lodash.forEach(dependencies, (value, key) => {
|
450
|
+
// eslint-disable-next-line no-param-reassign
|
451
|
+
seed[key] = value;
|
452
|
+
});
|
453
|
+
return next();
|
454
|
+
});
|
455
|
+
},
|
456
|
+
(next) => findExisting(seed, next),
|
457
|
+
(found, next) => mergeOne(found, seed, next),
|
458
|
+
],
|
459
|
+
afterUpsert
|
460
|
+
);
|
461
|
+
};
|
462
|
+
|
463
|
+
// prepare seeds
|
464
|
+
seeds = lodash.map(seeds, (seed) => {
|
465
|
+
return (next) => upsertOne(seed, next);
|
466
|
+
});
|
467
|
+
|
468
|
+
// run seeds
|
469
|
+
return async.parallel(seeds, cb);
|
470
|
+
}
|
471
|
+
|
472
|
+
/**
|
473
|
+
* @function clearAndSeedModel
|
474
|
+
* @name clearAndSeedModel
|
475
|
+
* @description clear and seed the given model data
|
476
|
+
* @param {object|object[]|Function} data valid model data
|
477
|
+
* @param {Function} done callback to invoke on success or error
|
478
|
+
* @returns {object} seed results
|
479
|
+
* @since 0.21.0
|
480
|
+
* @version 0.2.0
|
481
|
+
* @private
|
482
|
+
*/
|
483
|
+
function clearAndSeedModel(data, done) {
|
484
|
+
// this: Model static context
|
485
|
+
|
486
|
+
// normalize callback
|
487
|
+
const cb = lodash.isFunction(data) ? data : done;
|
488
|
+
|
489
|
+
// clear model data
|
490
|
+
const doClear = (next) => this.deleteMany((error) => next(error));
|
491
|
+
|
492
|
+
// seed model data
|
493
|
+
const doSeed = (next) =>
|
494
|
+
lodash.isFunction(data) ? this.seed(next) : this.seed(data, next);
|
495
|
+
|
496
|
+
// run clear then seed
|
497
|
+
return async.waterfall([doClear, doSeed], cb);
|
498
|
+
}
|
499
|
+
|
500
|
+
/**
|
501
|
+
* @function seedPlugin
|
502
|
+
* @name seedPlugin
|
503
|
+
* @description Extend mongoose schema with seed capability
|
504
|
+
* @param {object} schema valid mongoose schema instance
|
505
|
+
* @since 0.21.0
|
506
|
+
* @version 0.2.0
|
507
|
+
* @public
|
508
|
+
*/
|
509
|
+
function seedPlugin(schema) {
|
510
|
+
const canNotSeed = !lodash.isFunction(schema.statics.seed);
|
511
|
+
if (canNotSeed) {
|
512
|
+
// eslint-disable-next-line no-param-reassign
|
513
|
+
schema.statics.seed = seedModel;
|
514
|
+
|
515
|
+
// eslint-disable-next-line no-param-reassign
|
516
|
+
schema.statics.clearAndSeed = clearAndSeedModel;
|
517
|
+
}
|
518
|
+
}
|
519
|
+
|
520
|
+
// TODO: async prepareSeedCriteria
|
521
|
+
// TODO: prepareSeedCriteria(seed, code)
|
522
|
+
// TODO: done(error, criteria)
|
523
|
+
|
524
|
+
/**
|
525
|
+
* @module mongoose-common
|
526
|
+
* @name mongoose-common
|
527
|
+
* @description Re-usable helpers for mongoose
|
528
|
+
* @author lally elias <lallyelias87@mail.com>
|
529
|
+
* @license MIT
|
530
|
+
* @since 0.1.0
|
531
|
+
* @version 0.1.0
|
532
|
+
* @public
|
533
|
+
* @example
|
534
|
+
*
|
535
|
+
* const {
|
536
|
+
* connect,
|
537
|
+
* clear,
|
538
|
+
* drop,
|
539
|
+
* disconnect,
|
540
|
+
* } = require('@lykmapipo/mongoose-common');
|
541
|
+
*
|
542
|
+
* connect((error) => { ... });
|
543
|
+
* clear((error) => { ... });
|
544
|
+
* drop((error) => { ... });
|
545
|
+
* disconnect((error) => { ... });
|
546
|
+
*/
|
547
|
+
|
548
|
+
// set global mongoose promise
|
549
|
+
mongoose.Promise = global.Promise;
|
550
|
+
|
551
|
+
/**
|
552
|
+
* @name path
|
553
|
+
* @description register path schema plugin
|
554
|
+
* @since 0.1.0
|
555
|
+
* @version 0.1.0
|
556
|
+
* @public
|
557
|
+
* @example
|
558
|
+
*
|
559
|
+
* const name = User.path('name');
|
560
|
+
* //=> SchemaString { path: 'name', instance: 'String', ... }
|
561
|
+
*/
|
562
|
+
mongoose.plugin(pathPlugin); // TODO: ignore global
|
563
|
+
|
564
|
+
/**
|
565
|
+
* @name error
|
566
|
+
* @description unique error handler schema plugin
|
567
|
+
* @since 0.1.0
|
568
|
+
* @version 0.1.0
|
569
|
+
* @public
|
570
|
+
*/
|
571
|
+
mongoose.plugin(uniqueErrorPlugin); // TODO: ignore global
|
572
|
+
|
573
|
+
/**
|
574
|
+
* @name seed
|
575
|
+
* @description data seed schema plugin
|
576
|
+
* @since 0.21.0
|
577
|
+
* @version 0.1.0
|
578
|
+
* @public
|
579
|
+
*/
|
580
|
+
mongoose.plugin(seedPlugin); // TODO: ignore global
|
581
|
+
|
582
|
+
// expose shortcuts
|
583
|
+
const { STATES } = mongoose;
|
584
|
+
const { Aggregate } = mongoose;
|
585
|
+
const { Collection } = mongoose;
|
586
|
+
const { Connection } = mongoose;
|
587
|
+
const { Schema } = mongoose;
|
588
|
+
const { SchemaType } = mongoose;
|
589
|
+
const { SchemaTypes } = mongoose;
|
590
|
+
const { VirtualType } = mongoose;
|
591
|
+
const { Types } = mongoose;
|
592
|
+
const MongooseTypes = mongoose.Types;
|
593
|
+
const { Query } = mongoose;
|
594
|
+
const MongooseError = mongoose.Error;
|
595
|
+
const { CastError } = mongoose;
|
596
|
+
const modelNames = () => mongoose.modelNames();
|
597
|
+
const { GridFSBucket } = mongoose.mongo;
|
598
|
+
|
599
|
+
// schema types shortcuts
|
600
|
+
|
601
|
+
const SchemaString = Schema.Types.String;
|
602
|
+
const SchemaNumber = Schema.Types.Number;
|
603
|
+
const SchemaBoolean = Schema.Types.Boolean;
|
604
|
+
const { DocumentArray } = Schema.Types;
|
605
|
+
const SchemaDocumentArray = Schema.Types.DocumentArray;
|
606
|
+
const SubDocument = Schema.Types.Subdocument;
|
607
|
+
const SchemaSubDocument = Schema.Types.Subdocument;
|
608
|
+
const Embedded = SubDocument;
|
609
|
+
const SchemaEmbedded = SubDocument;
|
610
|
+
const SchemaArray = Schema.Types.Array;
|
611
|
+
const SchemaBuffer = Schema.Types.Buffer;
|
612
|
+
const SchemaDate = Schema.Types.Date;
|
613
|
+
const { ObjectId } = Schema.Types;
|
614
|
+
const SchemaObjectId = Schema.Types.ObjectId;
|
615
|
+
const { Mixed } = Schema.Types;
|
616
|
+
const SchemaMixed = Schema.Types.Mixed;
|
617
|
+
const { Decimal128 } = Schema.Types;
|
618
|
+
const SchemaDecimal = Decimal128;
|
619
|
+
const SchemaDecimal128 = Decimal128;
|
620
|
+
const SchemaMap = Schema.Types.Map;
|
621
|
+
|
622
|
+
/**
|
623
|
+
* @name LOOKUP_FIELDS
|
624
|
+
* @description Common lookup fields used in aggregation
|
625
|
+
* @author lally elias <lallyelias87@mail.com>
|
626
|
+
* @since 0.13.0
|
627
|
+
* @version 0.1.0
|
628
|
+
* @public
|
629
|
+
* @example
|
630
|
+
*
|
631
|
+
* const { LOOKUP_FIELDS } = require('@lykmapipo/mongoose-common');
|
632
|
+
* //=> ['from', 'localField', 'foreignField', 'as']
|
633
|
+
*/
|
634
|
+
const LOOKUP_FIELDS = ['from', 'localField', 'foreignField', 'as'];
|
635
|
+
|
636
|
+
/**
|
637
|
+
* @function toCollectionName
|
638
|
+
* @name toCollectionName
|
639
|
+
* @description Produces a collection name of provided model name
|
640
|
+
* @param {string} modelName a model name
|
641
|
+
* @returns {string} a collection name
|
642
|
+
* @author lally elias <lallyelias87@mail.com>
|
643
|
+
* @since 0.8.0
|
644
|
+
* @version 0.1.0
|
645
|
+
* @public
|
646
|
+
* @example
|
647
|
+
*
|
648
|
+
* const collectionName = toCollectionName('User');
|
649
|
+
* //=> users
|
650
|
+
*/
|
651
|
+
const toCollectionName = (modelName) => {
|
652
|
+
let collectionName = modelName;
|
653
|
+
if (!lodash.isEmpty(modelName)) {
|
654
|
+
collectionName = mongoose.pluralize()(modelName);
|
655
|
+
}
|
656
|
+
return collectionName;
|
657
|
+
};
|
658
|
+
|
659
|
+
/**
|
660
|
+
* @function isObjectId
|
661
|
+
* @name isObjectId
|
662
|
+
* @description Check if provided value is an instance of ObjectId
|
663
|
+
* @param {Mixed} val value to check if its an ObjectId
|
664
|
+
* @author lally elias <lallyelias87@mail.com>
|
665
|
+
* @returns {boolean} whether a val is ObjectId instance
|
666
|
+
* @since 0.2.0
|
667
|
+
* @version 0.1.0
|
668
|
+
* @public
|
669
|
+
* @example
|
670
|
+
*
|
671
|
+
* isObjectId(val);
|
672
|
+
* //=> true
|
673
|
+
*/
|
674
|
+
const isObjectId = (val) => {
|
675
|
+
const $isObjectId = val instanceof mongoose.Types.ObjectId;
|
676
|
+
return $isObjectId;
|
677
|
+
};
|
678
|
+
|
679
|
+
/**
|
680
|
+
* @function isMap
|
681
|
+
* @name isMap
|
682
|
+
* @description Check if provided value is an instance of Map
|
683
|
+
* @param {Mixed} val value to check if its a Map
|
684
|
+
* @author lally elias <lallyelias87@mail.com>
|
685
|
+
* @returns {boolean} whether a val is Map instance
|
686
|
+
* @since 0.2.0
|
687
|
+
* @version 0.1.0
|
688
|
+
* @public
|
689
|
+
* @example
|
690
|
+
*
|
691
|
+
* isMap(val);
|
692
|
+
* //=> true
|
693
|
+
*/
|
694
|
+
const isMap = (val) => {
|
695
|
+
const $isMap = val instanceof mongoose.Types.Map;
|
696
|
+
return $isMap;
|
697
|
+
};
|
698
|
+
|
699
|
+
/**
|
700
|
+
* @function isString
|
701
|
+
* @name isString
|
702
|
+
* @description Check if provided value is an instance of String schema type
|
703
|
+
* @param {Mixed} val value to check if its a String schema type
|
704
|
+
* @author lally elias <lallyelias87@mail.com>
|
705
|
+
* @returns {boolean} whether a val is String instance
|
706
|
+
* @since 0.10.0
|
707
|
+
* @version 0.1.0
|
708
|
+
* @public
|
709
|
+
* @example
|
710
|
+
*
|
711
|
+
* isString(val);
|
712
|
+
* //=> true
|
713
|
+
*/
|
714
|
+
const isString = (val) => {
|
715
|
+
const $isString = val instanceof Schema.Types.String;
|
716
|
+
return $isString;
|
717
|
+
};
|
718
|
+
|
719
|
+
/**
|
720
|
+
* @function isArraySchemaType
|
721
|
+
* @name isArraySchemaType
|
722
|
+
* @description check if schema type is array
|
723
|
+
* @param {SchemaType} val valid mongoose schema type
|
724
|
+
* @returns {boolean} whether schema type is array
|
725
|
+
* @author lally elias <lallyelias87@mail.com>
|
726
|
+
* @since 0.16.0
|
727
|
+
* @version 0.1.0
|
728
|
+
* @public
|
729
|
+
* @example
|
730
|
+
*
|
731
|
+
* isArraySchemaType(val)
|
732
|
+
* //=> true
|
733
|
+
*/
|
734
|
+
const isArraySchemaType = (val = {}) => {
|
735
|
+
const { $isMongooseArray = false, instance } = val;
|
736
|
+
const isArray =
|
737
|
+
val instanceof Schema.Types.Array ||
|
738
|
+
$isMongooseArray ||
|
739
|
+
instance === 'Array';
|
740
|
+
return isArray;
|
741
|
+
};
|
742
|
+
|
743
|
+
/**
|
744
|
+
* @function isStringArray
|
745
|
+
* @name isStringArray
|
746
|
+
* @description Check if provided value is an instance of StringArray
|
747
|
+
* schema type
|
748
|
+
* @param {Mixed} val value to check if its a StringArray schema type
|
749
|
+
* @author lally elias <lallyelias87@mail.com>
|
750
|
+
* @returns {boolean} whether a val is String Array instance
|
751
|
+
* @since 0.11.0
|
752
|
+
* @version 0.1.0
|
753
|
+
* @public
|
754
|
+
* @example
|
755
|
+
*
|
756
|
+
* isStringArray(val);
|
757
|
+
* //=> true
|
758
|
+
*/
|
759
|
+
const isStringArray = (val) => {
|
760
|
+
const $isStringArray =
|
761
|
+
val &&
|
762
|
+
val instanceof Schema.Types.Array &&
|
763
|
+
val.caster instanceof Schema.Types.String;
|
764
|
+
return $isStringArray;
|
765
|
+
};
|
766
|
+
|
767
|
+
/**
|
768
|
+
* @function isNumber
|
769
|
+
* @name isNumber
|
770
|
+
* @description Check if provided value is an instance of Number schema type
|
771
|
+
* @param {Mixed} val value to check if its a Number schema type
|
772
|
+
* @author lally elias <lallyelias87@mail.com>
|
773
|
+
* @returns {boolean} whether a val is Number instance
|
774
|
+
* @since 0.10.0
|
775
|
+
* @version 0.1.0
|
776
|
+
* @public
|
777
|
+
* @example
|
778
|
+
*
|
779
|
+
* isNumber(<val>);
|
780
|
+
* //=> true
|
781
|
+
*/
|
782
|
+
const isNumber = (val) => {
|
783
|
+
const $isNumber = val instanceof Schema.Types.Number;
|
784
|
+
return $isNumber;
|
785
|
+
};
|
786
|
+
|
787
|
+
/**
|
788
|
+
* @function isNumberArray
|
789
|
+
* @name isNumberArray
|
790
|
+
* @description Check if provided value is an instance of NumberArray
|
791
|
+
* schema type
|
792
|
+
* @param {Mixed} val value to check if its a NumberArray schema type
|
793
|
+
* @author lally elias <lallyelias87@mail.com>
|
794
|
+
* @returns {boolean} whether a val is Number Array instance
|
795
|
+
* @since 0.11.0
|
796
|
+
* @version 0.1.0
|
797
|
+
* @public
|
798
|
+
* @example
|
799
|
+
*
|
800
|
+
* isNumberArray(val);
|
801
|
+
* //=> true
|
802
|
+
*/
|
803
|
+
const isNumberArray = (val) => {
|
804
|
+
const $isNumberArray =
|
805
|
+
val &&
|
806
|
+
val instanceof Schema.Types.Array &&
|
807
|
+
val.caster instanceof Schema.Types.Number;
|
808
|
+
return $isNumberArray;
|
809
|
+
};
|
810
|
+
|
811
|
+
/**
|
812
|
+
* @function isInstance
|
813
|
+
* @name isInstance
|
814
|
+
* @description check if object is valid mongoose model instance
|
815
|
+
* @param {object} value valid object
|
816
|
+
* @returns {boolean} whether object is valid model instance
|
817
|
+
* @author lally elias <lallyelias87@mail.com>
|
818
|
+
* @since 0.4.0
|
819
|
+
* @version 0.2.0
|
820
|
+
* @public
|
821
|
+
* @example
|
822
|
+
*
|
823
|
+
* isInstance(val);
|
824
|
+
* //=> true
|
825
|
+
*/
|
826
|
+
const isInstance = (value) => {
|
827
|
+
if (value) {
|
828
|
+
const $isInstance =
|
829
|
+
lodash.isFunction(lodash.get(value, 'toObject', null)) &&
|
830
|
+
!lodash.isNull(lodash.get(value, '$__', null));
|
831
|
+
return $isInstance;
|
832
|
+
}
|
833
|
+
return false;
|
834
|
+
};
|
835
|
+
|
836
|
+
/**
|
837
|
+
* @name copyInstance
|
838
|
+
* @description copy and return plain object of mongoose model instance
|
839
|
+
* @param {object} value valid object
|
840
|
+
* @returns {object} plain object from mongoose model instance
|
841
|
+
* @author lally elias <lallyelias87@mail.com>
|
842
|
+
* @since 0.4.0
|
843
|
+
* @version 0.1.0
|
844
|
+
* @public
|
845
|
+
* @example
|
846
|
+
*
|
847
|
+
* const instance = copyInstance(val);
|
848
|
+
* //=> { ... }
|
849
|
+
*/
|
850
|
+
const copyInstance = (value = {}) => common.mergeObjects(utils.toObject(value));
|
851
|
+
|
852
|
+
/**
|
853
|
+
* @function schemaTypeOptionOf
|
854
|
+
* @name schemaTypeOptionOf
|
855
|
+
* @description obtain schema type options
|
856
|
+
* @param {SchemaType} schemaType valid mongoose schema type
|
857
|
+
* @returns {object} schema type options
|
858
|
+
* @author lally elias <lallyelias87@mail.com>
|
859
|
+
* @since 0.14.0
|
860
|
+
* @version 0.1.0
|
861
|
+
* @private
|
862
|
+
* @example
|
863
|
+
*
|
864
|
+
* const options = schemaTypeOptionOf(schemaType)
|
865
|
+
* //=> { trim: true, ... }
|
866
|
+
*/
|
867
|
+
const schemaTypeOptionOf = (schemaType = {}) => {
|
868
|
+
// grab options
|
869
|
+
const options = common.mergeObjects(
|
870
|
+
// grub schema caster options
|
871
|
+
lodash.toPlainObject(lodash.get(schemaType, 'caster.options')),
|
872
|
+
// grab direct schema options
|
873
|
+
lodash.toPlainObject(lodash.get(schemaType, 'options'))
|
874
|
+
);
|
875
|
+
// return options
|
876
|
+
return options;
|
877
|
+
};
|
878
|
+
|
879
|
+
/**
|
880
|
+
* @function eachPath
|
881
|
+
* @name eachPath
|
882
|
+
* @description iterate recursively on schema primitive paths and invoke
|
883
|
+
* provided iteratee function.
|
884
|
+
* @param {object} schema valid instance of mongoose schema
|
885
|
+
* @param {Function} iteratee callback function invoked per each path found.
|
886
|
+
* The callback is passed the pathName, parentPath and schemaType as arguments
|
887
|
+
* on each iteration.
|
888
|
+
* @see {@link https://mongoosejs.com/docs/api.html#schema_Schema-eachPath}
|
889
|
+
* @author lally elias <lallyelias87@mail.com>
|
890
|
+
* @since 0.1.0
|
891
|
+
* @version 0.1.0
|
892
|
+
* @public
|
893
|
+
* @example
|
894
|
+
*
|
895
|
+
* eachPath(schema, (path, schemaType) => { ... });
|
896
|
+
*/
|
897
|
+
const eachPath = (schema, iteratee) => {
|
898
|
+
/**
|
899
|
+
* @name iterateRecursive
|
900
|
+
* @description recursivily search for a schema path
|
901
|
+
* @param {string} pathName valid schema path name
|
902
|
+
* @param {object} schemaType valid schema type
|
903
|
+
* @param {string} parentPath parent schema path
|
904
|
+
*/
|
905
|
+
function iterateRecursive(pathName, schemaType, parentPath) {
|
906
|
+
// compute path name
|
907
|
+
const $path = common.compact([parentPath, pathName]).join('.');
|
908
|
+
|
909
|
+
// check if is sub schema
|
910
|
+
const $isSchema =
|
911
|
+
schemaType.schema && lodash.isFunction(schemaType.schema.eachPath);
|
912
|
+
|
913
|
+
// iterate over sub schema
|
914
|
+
if ($isSchema) {
|
915
|
+
const { schema: subSchema } = schemaType;
|
916
|
+
subSchema.eachPath(function iterateSubSchema($pathName, $schemaType) {
|
917
|
+
iterateRecursive($pathName, $schemaType, $path);
|
918
|
+
});
|
919
|
+
}
|
920
|
+
|
921
|
+
// invoke iteratee
|
922
|
+
else {
|
923
|
+
iteratee($path, schemaType);
|
924
|
+
}
|
925
|
+
}
|
926
|
+
|
927
|
+
// iterate recursive
|
928
|
+
schema.eachPath(function iterateParentSchema(pathName, schemaType) {
|
929
|
+
iterateRecursive(pathName, schemaType);
|
930
|
+
});
|
931
|
+
};
|
932
|
+
|
933
|
+
/**
|
934
|
+
* @function jsonSchema
|
935
|
+
* @name jsonSchema
|
936
|
+
* @description Produces valid json schema of all available models
|
937
|
+
* if `mongoose-schema-jsonschema` has been applied
|
938
|
+
* @author lally elias <lallyelias87@mail.com>
|
939
|
+
* @returns {object[]} models json schema
|
940
|
+
* @since 0.8.0
|
941
|
+
* @version 0.1.0
|
942
|
+
* @public
|
943
|
+
* @example
|
944
|
+
*
|
945
|
+
* const jsonSchema = jsonSchema();
|
946
|
+
* //=> {"user": {title: "User", type: "object", properties: {..} } }
|
947
|
+
*/
|
948
|
+
const jsonSchema = () => {
|
949
|
+
// initialize schemas dictionary
|
950
|
+
const schemas = {};
|
951
|
+
// get model names
|
952
|
+
const $modelNames = mongoose.modelNames();
|
953
|
+
// loop model names to get schemas
|
954
|
+
lodash.forEach($modelNames, function getJsonSchema(modelName) {
|
955
|
+
// get model
|
956
|
+
const Model = mongooseConnection.model(modelName);
|
957
|
+
// collect model json schema
|
958
|
+
if (Model && lodash.isFunction(Model.jsonSchema)) {
|
959
|
+
schemas[modelName] = Model.jsonSchema();
|
960
|
+
}
|
961
|
+
});
|
962
|
+
// return available schemas
|
963
|
+
return schemas;
|
964
|
+
};
|
965
|
+
|
966
|
+
/**
|
967
|
+
* @function validationErrorFor
|
968
|
+
* @name validationErrorFor
|
969
|
+
* @description Create mongoose validation error for specified options
|
970
|
+
* @param {object} optns valid error options
|
971
|
+
* @param {number | string} [optns.status] valid error status
|
972
|
+
* @param {number | string} [optns.code] valid error code
|
973
|
+
* @param {object} [optns.paths] paths with validator error properties
|
974
|
+
* @returns {object} valid instance of mongoose validation error
|
975
|
+
* @author lally elias <lallyelias87@mail.com>
|
976
|
+
* @since 0.24.0
|
977
|
+
* @version 0.1.0
|
978
|
+
* @public
|
979
|
+
* @example
|
980
|
+
*
|
981
|
+
* const status = 400;
|
982
|
+
* const paths = {
|
983
|
+
* name: { type: 'required', path:'name', value: ..., message: ... }
|
984
|
+
* };
|
985
|
+
* const error = validationErrorFor({ status, paths });
|
986
|
+
* //=> error
|
987
|
+
*/
|
988
|
+
const validationErrorFor = (optns) => {
|
989
|
+
// obtain options
|
990
|
+
const { status = 400, code = 400, paths = {} } = common.mergeObjects(optns);
|
991
|
+
|
992
|
+
// create mongoose validation error
|
993
|
+
const error = new mongoose.Error.ValidationError();
|
994
|
+
error.status = status;
|
995
|
+
error.code = code || status;
|
996
|
+
// eslint-disable-next-line no-underscore-dangle
|
997
|
+
error.message = error.message || error._message;
|
998
|
+
|
999
|
+
// attach path validator error
|
1000
|
+
if (!lodash.isEmpty(paths)) {
|
1001
|
+
const errors = {};
|
1002
|
+
lodash.forEach(paths, (props, path) => {
|
1003
|
+
let pathError = common.mergeObjects({ path }, props);
|
1004
|
+
pathError = new mongoose.Error.ValidatorError(pathError);
|
1005
|
+
errors[path] = pathError;
|
1006
|
+
});
|
1007
|
+
error.errors = errors;
|
1008
|
+
}
|
1009
|
+
|
1010
|
+
// return validation error
|
1011
|
+
return error;
|
1012
|
+
};
|
1013
|
+
|
1014
|
+
/**
|
1015
|
+
* @function areSameInstance
|
1016
|
+
* @name areSameInstance
|
1017
|
+
* @description check if given two mongoose model instances are same
|
1018
|
+
* @param {object} a valid model instance
|
1019
|
+
* @param {object} b valid model instance
|
1020
|
+
* @returns {boolean} whether model instance are same
|
1021
|
+
* @author lally elias <lallyelias87@mail.com>
|
1022
|
+
* @since 0.31.0
|
1023
|
+
* @version 0.1.0
|
1024
|
+
* @public
|
1025
|
+
* @example
|
1026
|
+
*
|
1027
|
+
* areSameInstance(a, a); //=> true
|
1028
|
+
*/
|
1029
|
+
const areSameInstance = (a, b) => {
|
1030
|
+
try {
|
1031
|
+
const areSame = !!(a && b && a.equals(b));
|
1032
|
+
return areSame;
|
1033
|
+
} catch (e) {
|
1034
|
+
return false;
|
1035
|
+
}
|
1036
|
+
};
|
1037
|
+
|
1038
|
+
/**
|
1039
|
+
* @function areSameObjectId
|
1040
|
+
* @name areSameObjectId
|
1041
|
+
* @description check if given two mongoose objectid are same
|
1042
|
+
* @param {object} a valid object id
|
1043
|
+
* @param {object} b valid object
|
1044
|
+
* @returns {boolean} whether objectid's are same
|
1045
|
+
* @author lally elias <lallyelias87@mail.com>
|
1046
|
+
* @since 0.31.0
|
1047
|
+
* @version 0.1.0
|
1048
|
+
* @public
|
1049
|
+
* @example
|
1050
|
+
*
|
1051
|
+
* areSameObjectId(a, a); //=> true
|
1052
|
+
*/
|
1053
|
+
const areSameObjectId = (a, b) => {
|
1054
|
+
try {
|
1055
|
+
// grab actual ids
|
1056
|
+
const idOfA = common.idOf(a) || a;
|
1057
|
+
const idOfB = common.idOf(b) || b;
|
1058
|
+
|
1059
|
+
// convert to string
|
1060
|
+
const idA = isObjectId(idOfA) ? idOfA.toString() : idOfA;
|
1061
|
+
const idB = isObjectId(idOfB) ? idOfB.toString() : idOfB;
|
1062
|
+
|
1063
|
+
// check if are equal
|
1064
|
+
const areSame = idA === idB;
|
1065
|
+
return areSame;
|
1066
|
+
} catch (e) {
|
1067
|
+
return false;
|
1068
|
+
}
|
1069
|
+
};
|
1070
|
+
|
1071
|
+
/**
|
1072
|
+
* @function toObjectIds
|
1073
|
+
* @name toObjectIds
|
1074
|
+
* @description convert given model instances into object ids
|
1075
|
+
* @param {...object} instances valid model instances
|
1076
|
+
* @returns {object[]} objectid's of model instances
|
1077
|
+
* @author lally elias <lallyelias87@mail.com>
|
1078
|
+
* @since 0.31.0
|
1079
|
+
* @version 0.1.0
|
1080
|
+
* @public
|
1081
|
+
* @example
|
1082
|
+
*
|
1083
|
+
* toObjectIds(a, b); //=> [ '5e90486301de071ca4ebc03d', ... ]
|
1084
|
+
*/
|
1085
|
+
const toObjectIds = (...instances) => {
|
1086
|
+
const ids = lodash.map([...instances], (instance) => {
|
1087
|
+
const id = common.idOf(instance) || instance;
|
1088
|
+
return id;
|
1089
|
+
});
|
1090
|
+
return ids;
|
1091
|
+
};
|
1092
|
+
|
1093
|
+
/**
|
1094
|
+
* @function toObjectIdStrings
|
1095
|
+
* @name toObjectIdStrings
|
1096
|
+
* @description convert given model instances objectid's into strings
|
1097
|
+
* @param {...object} instances valid model instances
|
1098
|
+
* @returns {string[]} objectid's as strings
|
1099
|
+
* @author lally elias <lallyelias87@mail.com>
|
1100
|
+
* @since 0.31.0
|
1101
|
+
* @version 0.1.0
|
1102
|
+
* @public
|
1103
|
+
* @example
|
1104
|
+
*
|
1105
|
+
* toObjectIdStrings(a, b); //=> [ '5e90486301de071ca4ebc03d', ... ]
|
1106
|
+
*/
|
1107
|
+
const toObjectIdStrings = (...instances) => {
|
1108
|
+
const ids = toObjectIds(...instances);
|
1109
|
+
const idStrings = lodash.map([...ids], (id) => {
|
1110
|
+
const idString = isObjectId(id) ? id.toString() : id;
|
1111
|
+
return idString;
|
1112
|
+
});
|
1113
|
+
return idStrings;
|
1114
|
+
};
|
1115
|
+
|
1116
|
+
/**
|
1117
|
+
* @function objectIdFor
|
1118
|
+
* @name objectIdFor
|
1119
|
+
* @description create a unique objectid of a given model values
|
1120
|
+
* @param {...string} modelName valid model name
|
1121
|
+
* @param {...string} parts values to generate object id for
|
1122
|
+
* @returns {object} valid objectid
|
1123
|
+
* @author lally elias <lallyelias87@mail.com>
|
1124
|
+
* @since 0.36.0
|
1125
|
+
* @version 0.1.0
|
1126
|
+
* @public
|
1127
|
+
* @example
|
1128
|
+
*
|
1129
|
+
* objectIdFor('Party', 'TZ-0101');
|
1130
|
+
* //=> '5e90486301de071ca4ebc03d'
|
1131
|
+
*/
|
1132
|
+
const objectIdFor = (modelName, ...parts) => {
|
1133
|
+
// ensure parts
|
1134
|
+
const values = common.compact([].concat(modelName).concat(...parts));
|
1135
|
+
|
1136
|
+
// ensure secret & message
|
1137
|
+
const secret = lodash.head(values);
|
1138
|
+
const data = common.join(lodash.tail(values), ':');
|
1139
|
+
|
1140
|
+
// generate 24-byte hex hash
|
1141
|
+
const hash = crypto.createHmac('md5', secret)
|
1142
|
+
.update(data)
|
1143
|
+
.digest('hex')
|
1144
|
+
.slice(0, 24);
|
1145
|
+
|
1146
|
+
// create objectid from hash
|
1147
|
+
const objectId = mongoose.Types.ObjectId.createFromHexString(hash);
|
1148
|
+
|
1149
|
+
return objectId;
|
1150
|
+
};
|
1151
|
+
|
1152
|
+
Object.defineProperty(exports, 'SCHEMA_OPTIONS', {
|
1153
|
+
enumerable: true,
|
1154
|
+
get: function () { return mongooseConnection.SCHEMA_OPTIONS; }
|
1155
|
+
});
|
1156
|
+
Object.defineProperty(exports, 'SUB_SCHEMA_OPTIONS', {
|
1157
|
+
enumerable: true,
|
1158
|
+
get: function () { return mongooseConnection.SUB_SCHEMA_OPTIONS; }
|
1159
|
+
});
|
1160
|
+
Object.defineProperty(exports, 'clear', {
|
1161
|
+
enumerable: true,
|
1162
|
+
get: function () { return mongooseConnection.clear; }
|
1163
|
+
});
|
1164
|
+
Object.defineProperty(exports, 'collectionNameOf', {
|
1165
|
+
enumerable: true,
|
1166
|
+
get: function () { return mongooseConnection.collectionNameOf; }
|
1167
|
+
});
|
1168
|
+
Object.defineProperty(exports, 'connect', {
|
1169
|
+
enumerable: true,
|
1170
|
+
get: function () { return mongooseConnection.connect; }
|
1171
|
+
});
|
1172
|
+
Object.defineProperty(exports, 'createModel', {
|
1173
|
+
enumerable: true,
|
1174
|
+
get: function () { return mongooseConnection.createModel; }
|
1175
|
+
});
|
1176
|
+
Object.defineProperty(exports, 'createSchema', {
|
1177
|
+
enumerable: true,
|
1178
|
+
get: function () { return mongooseConnection.createSchema; }
|
1179
|
+
});
|
1180
|
+
Object.defineProperty(exports, 'createSubSchema', {
|
1181
|
+
enumerable: true,
|
1182
|
+
get: function () { return mongooseConnection.createSubSchema; }
|
1183
|
+
});
|
1184
|
+
Object.defineProperty(exports, 'createVarySubSchema', {
|
1185
|
+
enumerable: true,
|
1186
|
+
get: function () { return mongooseConnection.createVarySubSchema; }
|
1187
|
+
});
|
1188
|
+
Object.defineProperty(exports, 'disableDebug', {
|
1189
|
+
enumerable: true,
|
1190
|
+
get: function () { return mongooseConnection.disableDebug; }
|
1191
|
+
});
|
1192
|
+
Object.defineProperty(exports, 'disconnect', {
|
1193
|
+
enumerable: true,
|
1194
|
+
get: function () { return mongooseConnection.disconnect; }
|
1195
|
+
});
|
1196
|
+
Object.defineProperty(exports, 'drop', {
|
1197
|
+
enumerable: true,
|
1198
|
+
get: function () { return mongooseConnection.drop; }
|
1199
|
+
});
|
1200
|
+
Object.defineProperty(exports, 'enableDebug', {
|
1201
|
+
enumerable: true,
|
1202
|
+
get: function () { return mongooseConnection.enableDebug; }
|
1203
|
+
});
|
1204
|
+
Object.defineProperty(exports, 'isAggregate', {
|
1205
|
+
enumerable: true,
|
1206
|
+
get: function () { return mongooseConnection.isAggregate; }
|
1207
|
+
});
|
1208
|
+
Object.defineProperty(exports, 'isConnected', {
|
1209
|
+
enumerable: true,
|
1210
|
+
get: function () { return mongooseConnection.isConnected; }
|
1211
|
+
});
|
1212
|
+
Object.defineProperty(exports, 'isConnection', {
|
1213
|
+
enumerable: true,
|
1214
|
+
get: function () { return mongooseConnection.isConnection; }
|
1215
|
+
});
|
1216
|
+
Object.defineProperty(exports, 'isModel', {
|
1217
|
+
enumerable: true,
|
1218
|
+
get: function () { return mongooseConnection.isModel; }
|
1219
|
+
});
|
1220
|
+
Object.defineProperty(exports, 'isQuery', {
|
1221
|
+
enumerable: true,
|
1222
|
+
get: function () { return mongooseConnection.isQuery; }
|
1223
|
+
});
|
1224
|
+
Object.defineProperty(exports, 'isSchema', {
|
1225
|
+
enumerable: true,
|
1226
|
+
get: function () { return mongooseConnection.isSchema; }
|
1227
|
+
});
|
1228
|
+
Object.defineProperty(exports, 'model', {
|
1229
|
+
enumerable: true,
|
1230
|
+
get: function () { return mongooseConnection.model; }
|
1231
|
+
});
|
1232
|
+
Object.defineProperty(exports, 'syncIndexes', {
|
1233
|
+
enumerable: true,
|
1234
|
+
get: function () { return mongooseConnection.syncIndexes; }
|
1235
|
+
});
|
1236
|
+
exports.Aggregate = Aggregate;
|
1237
|
+
exports.CastError = CastError;
|
1238
|
+
exports.Collection = Collection;
|
1239
|
+
exports.Connection = Connection;
|
1240
|
+
exports.Decimal128 = Decimal128;
|
1241
|
+
exports.DocumentArray = DocumentArray;
|
1242
|
+
exports.Embedded = Embedded;
|
1243
|
+
exports.GridFSBucket = GridFSBucket;
|
1244
|
+
exports.LOOKUP_FIELDS = LOOKUP_FIELDS;
|
1245
|
+
exports.Mixed = Mixed;
|
1246
|
+
exports.MongooseError = MongooseError;
|
1247
|
+
exports.MongooseTypes = MongooseTypes;
|
1248
|
+
exports.ObjectId = ObjectId;
|
1249
|
+
exports.Query = Query;
|
1250
|
+
exports.STATES = STATES;
|
1251
|
+
exports.Schema = Schema;
|
1252
|
+
exports.SchemaArray = SchemaArray;
|
1253
|
+
exports.SchemaBoolean = SchemaBoolean;
|
1254
|
+
exports.SchemaBuffer = SchemaBuffer;
|
1255
|
+
exports.SchemaDate = SchemaDate;
|
1256
|
+
exports.SchemaDecimal = SchemaDecimal;
|
1257
|
+
exports.SchemaDecimal128 = SchemaDecimal128;
|
1258
|
+
exports.SchemaDocumentArray = SchemaDocumentArray;
|
1259
|
+
exports.SchemaEmbedded = SchemaEmbedded;
|
1260
|
+
exports.SchemaMap = SchemaMap;
|
1261
|
+
exports.SchemaMixed = SchemaMixed;
|
1262
|
+
exports.SchemaNumber = SchemaNumber;
|
1263
|
+
exports.SchemaObjectId = SchemaObjectId;
|
1264
|
+
exports.SchemaString = SchemaString;
|
1265
|
+
exports.SchemaSubDocument = SchemaSubDocument;
|
1266
|
+
exports.SchemaType = SchemaType;
|
1267
|
+
exports.SchemaTypes = SchemaTypes;
|
1268
|
+
exports.SubDocument = SubDocument;
|
1269
|
+
exports.Types = Types;
|
1270
|
+
exports.VirtualType = VirtualType;
|
1271
|
+
exports.areSameInstance = areSameInstance;
|
1272
|
+
exports.areSameObjectId = areSameObjectId;
|
1273
|
+
exports.copyInstance = copyInstance;
|
1274
|
+
exports.eachPath = eachPath;
|
1275
|
+
exports.isArraySchemaType = isArraySchemaType;
|
1276
|
+
exports.isInstance = isInstance;
|
1277
|
+
exports.isMap = isMap;
|
1278
|
+
exports.isNumber = isNumber;
|
1279
|
+
exports.isNumberArray = isNumberArray;
|
1280
|
+
exports.isObjectId = isObjectId;
|
1281
|
+
exports.isString = isString;
|
1282
|
+
exports.isStringArray = isStringArray;
|
1283
|
+
exports.jsonSchema = jsonSchema;
|
1284
|
+
exports.modelNames = modelNames;
|
1285
|
+
exports.objectIdFor = objectIdFor;
|
1286
|
+
exports.schemaTypeOptionOf = schemaTypeOptionOf;
|
1287
|
+
exports.toCollectionName = toCollectionName;
|
1288
|
+
exports.toObjectIdStrings = toObjectIdStrings;
|
1289
|
+
exports.toObjectIds = toObjectIds;
|
1290
|
+
exports.validationErrorFor = validationErrorFor;
|