@conpago/mongo-cursor-pagination
Version:
Make it easy to return cursor-paginated results from a Mongo collection
83 lines • 4.35 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const projection_utils_1 = require("projection-utils");
const underscore_1 = __importDefault(require("underscore"));
/**
* Produce a ProjectionFieldSet from the given mongo projection, after validating it to ensure it
* doesn't have exclusion rules.
*
* @param {Object<String, *>} projection The projected fields.
* @param {Boolean=} includeIdDefault Whether to include _id by default (mongo's default behavior).
* @returns {ProjectionFieldSet} The synthesized field set.
*/
function fieldsFromMongo(projection = {}, includeIdDefault = false) {
const fields = underscore_1.default.reduce(projection, (memo, value, key) => {
if (key !== "_id" && value !== undefined && !value) {
throw new TypeError("projection includes exclusion, but we do not support that");
}
if (value || (key === "_id" && value === undefined && includeIdDefault)) {
memo.push(key);
}
return memo;
}, []);
return projection_utils_1.ProjectionFieldSet.fromDotted(fields);
}
/**
* Resolve the fields object, given potentially untrusted fields the user has provided, permitted
* fields defined by the application, and override fields that should always be provided.
*
* @param {String[]} desiredFields The fields in the request.
* @param {?Object<String, *>=} allowedFields A shallow fields object defining the fields permitted
* in desiredFields. If not provided, we just allow any field.
* @param {Object<String, *>=} overrideFields A shallow fields object defining fields that should
* always be configured as specified.
* @returns {?Object<String, *>=} The resolved fields declaration.
*/
function resolveFields(desiredFields, allowedFields, overrideFields) {
if (desiredFields != null && !Array.isArray(desiredFields)) {
throw new TypeError("expected nullable array for desiredFields");
}
if (allowedFields != null && !underscore_1.default.isObject(allowedFields)) {
throw new TypeError("expected nullable plain object for allowedFields");
}
if (overrideFields !== undefined && !underscore_1.default.isObject(overrideFields)) {
throw new TypeError("expected optional plain object for overrideFields");
}
// If no desired fields are specified, we treat that as wanting the default set of fields.
const desiredFieldset = underscore_1.default.isEmpty(desiredFields)
? new projection_utils_1.ProjectionFieldSet([[]])
: projection_utils_1.ProjectionFieldSet.fromDotted(desiredFields);
// If allowedFields isn't provided, we treat that as not having restrictions. However, if it's an
// empty array, we treat that as have no valid fields.
const allowedFieldset = allowedFields
? fieldsFromMongo(allowedFields)
: new projection_utils_1.ProjectionFieldSet([[]]);
// Don't trust fields passed in the querystring, so whitelist them against the
// fields defined in parameters. Add override fields from parameters.
const fields = desiredFieldset
.intersect(allowedFieldset)
.union(fieldsFromMongo(overrideFields));
if (fields.isEmpty()) {
// This projection isn't representable as a mongo projection - nor should it be. We don't want
// to query mongo for zero fields.
return null;
}
// Generate the mongo projection.
const projection = fields.toMongo();
// Whether overrideFields explicitly removes _id.
const disableIdOverride = overrideFields && overrideFields._id !== undefined && !overrideFields._id;
// Explicitly exclude the _id field (which mongo includes by default) if we don't allow it, or
// if we've disabled it in the override.
if (!fields.contains(["_id"]) || disableIdOverride) {
// If the override excludes _id, then enforce that here. All other fields will be included by
// default, so we don't need to specify them individually, as we only support whitelisting
// fields, and do not support field blacklists.
projection._id = 0;
}
return projection;
}
exports.default = resolveFields;
//# sourceMappingURL=resolveFields.js.map