copies autopopulate for our own usage

This commit is contained in:
James
2019-05-06 21:43:46 -04:00
parent 53c8844b86
commit e48643cf91
7 changed files with 188 additions and 28 deletions

View File

@@ -1,10 +1,16 @@
export function createAutopopulateOptions(depth) {
const autopopulateOptions = {};
export function createAutopopulateOptions({ depth, locale, fallback }) {
const autopopulateOptions = {
locale,
fallback
};
if (typeof depth != 'undefined') {
autopopulateOptions.maxDepth = depth;
if (depth === '0') {
autopopulateOptions.autopopulate = false;
}
}
return autopopulateOptions;
return {
autopopulate: autopopulateOptions
};
}

146
src/plugins/autopopulate.js Normal file
View File

@@ -0,0 +1,146 @@
'use strict';
const mongoose = require('mongoose');
module.exports = function (schema) {
const pathsToPopulate = [];
eachPathRecursive(schema, (pathname, schemaType) => {
let option;
if (schemaType.options && schemaType.options.autopopulate) {
option = schemaType.options.autopopulate;
pathsToPopulate.push({
options: defaultOptions(pathname, schemaType.options),
autopopulate: option
});
} else if (schemaType.options &&
schemaType.options.type &&
schemaType.options.type[0] &&
schemaType.options.type[0].autopopulate) {
option = schemaType.options.type[0].autopopulate;
pathsToPopulate.push({
options: defaultOptions(pathname, schemaType.options.type[0]),
autopopulate: option
});
}
});
if (schema.virtuals) {
Object.keys(schema.virtuals).forEach((pathname) => {
if (schema.virtuals[pathname].options.autopopulate) {
pathsToPopulate.push({
options: defaultOptions(pathname, schema.virtuals[pathname].options),
autopopulate: schema.virtuals[pathname].options.autopopulate,
});
}
});
}
var autopopulateHandler = function () {
if (this._mongooseOptions &&
this._mongooseOptions.lean &&
// If lean and user didn't explicitly do `lean({ autopulate: true })`,
// skip it. See gh-27, gh-14, gh-48
!this._mongooseOptions.lean.autopopulate) {
return;
}
const options = this.options || {};
if (options.autopopulate === false) {
return;
}
const depth = options._depth != null ? options._depth : 0;
if (options.maxDepth > 0 && depth >= options.maxDepth) {
return;
}
const numPaths = pathsToPopulate.length;
for (let i = 0; i < numPaths; ++i) {
pathsToPopulate[i].options = pathsToPopulate[i].options || {};
pathsToPopulate[i].options.options = pathsToPopulate[i].options.options || {};
Object.assign(pathsToPopulate[i].options.options, {
...options,
_depth: depth + 1
});
processOption.call(this,
pathsToPopulate[i].autopopulate, pathsToPopulate[i].options);
}
};
schema.
pre('find', autopopulateHandler).
pre('findOne', autopopulateHandler).
pre('findOneAndUpdate', autopopulateHandler);
};
function defaultOptions(pathname, v) {
const ret = { path: pathname, options: { maxDepth: 10 } };
if (v.ref != null) {
ret.model = v.ref;
ret.ref = v.ref;
}
if (v.refPath != null) {
ret.refPath = v.refPath;
}
return ret;
}
function processOption(value, options) {
switch (typeof value) {
case 'function':
handleFunction.call(this, value, options);
break;
case 'object':
handleObject.call(this, value, options);
break;
default:
handlePrimitive.call(this, value, options);
break;
}
}
function handlePrimitive(value, options) {
if (value) {
this.populate(options);
}
}
function handleObject(value, optionsToUse) {
// Special case: support top-level `maxDepth`
if (value.maxDepth != null) {
optionsToUse.options = optionsToUse.options || {};
optionsToUse.options.maxDepth = value.maxDepth;
delete value.maxDepth;
}
optionsToUse = Object.assign({}, optionsToUse, value);
this.populate(optionsToUse);
}
function handleFunction(fn, options) {
const val = fn.call(this, options);
processOption.call(this, val, options);
}
function mergeOptions(destination, source) {
const keys = Object.keys(source);
const numKeys = keys.length;
for (let i = 0; i < numKeys; ++i) {
destination[keys[i]] = source[keys[i]];
}
}
function eachPathRecursive(schema, handler, path) {
if (!path) {
path = [];
}
schema.eachPath((pathname, schemaType) => {
path.push(pathname);
if (schemaType.schema) {
eachPathRecursive(schemaType.schema, handler, path);
} else {
handler(path.join('.'), schemaType);
}
path.pop();
});
}

View File

@@ -19,6 +19,7 @@ export default function internationalization(schema, options) {
}
schema.eachPath((path, schemaType) => {
if (schemaType.schema) { // propagate plugin initialization for sub-documents schemas
schemaType.schema.plugin(internationalization, pluginOptions);
return;
@@ -51,6 +52,7 @@ export default function internationalization(schema, options) {
schema.virtual(path)
.get(function () {
// embedded and sub-documents will use locale methods from the top level document
let owner = this.ownerDocument ? this.ownerDocument() : this,
locale = owner.getLocale(),

View File

@@ -8,9 +8,11 @@ const findOne = (req, res) => {
Model: req.model,
id: req.params._id,
locale: req.locale,
fallback: req.query['fallback-locale']
fallback: req.query['fallback-locale'],
depth: req.query.depth
};
modelById(query, { ...createAutopopulateOptions(req.query.depth) })
modelById(query, createAutopopulateOptions(query))
.then(doc => res.json(doc))
.catch(err => res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err }));
};

View File

@@ -2,7 +2,7 @@ import httpStatus from 'http-status';
const query = (req, res) => {
req.model.paginate(req.model.apiQuery(req.query, req.locale), {...req.query}, (err, result) => {
req.model.paginate(req.model.apiQuery(req.query, req.locale), { ...req.query }, (err, result) => {
if (err) {
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err });
}