UNPKG

@conpago/mongo-cursor-pagination

Version:

Make it easy to return cursor-paginated results from a Mongo collection

83 lines 4.35 kB
"use strict"; 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