diff options
author | 2020-11-16 00:10:28 +0100 | |
---|---|---|
committer | 2020-11-16 00:10:28 +0100 | |
commit | e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d (patch) | |
tree | 55713f725f77b44ebfec86e4eec3ce33e71458ca /node_modules/mongoose/lib/helpers/update | |
download | website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.gz website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.bz2 website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.zip |
api, login, auth
Diffstat (limited to 'node_modules/mongoose/lib/helpers/update')
6 files changed, 473 insertions, 0 deletions
diff --git a/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js b/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js new file mode 100644 index 0000000..680d692 --- /dev/null +++ b/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js @@ -0,0 +1,172 @@ +'use strict'; + +const cleanPositionalOperators = require('../schema/cleanPositionalOperators'); +const handleTimestampOption = require('../schema/handleTimestampOption'); + +module.exports = applyTimestampsToChildren; + +/*! + * ignore + */ + +function applyTimestampsToChildren(now, update, schema) { + if (update == null) { + return; + } + + const keys = Object.keys(update); + const hasDollarKey = keys.some(key => key.startsWith('$')); + + if (hasDollarKey) { + if (update.$push) { + for (const key of Object.keys(update.$push)) { + const $path = schema.path(key); + if (update.$push[key] && + $path && + $path.$isMongooseDocumentArray && + $path.schema.options.timestamps) { + const timestamps = $path.schema.options.timestamps; + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + if (update.$push[key].$each) { + update.$push[key].$each.forEach(function(subdoc) { + if (updatedAt != null) { + subdoc[updatedAt] = now; + } + if (createdAt != null) { + subdoc[createdAt] = now; + } + }); + } else { + if (updatedAt != null) { + update.$push[key][updatedAt] = now; + } + if (createdAt != null) { + update.$push[key][createdAt] = now; + } + } + } + } + } + if (update.$set != null) { + const keys = Object.keys(update.$set); + for (const key of keys) { + applyTimestampsToUpdateKey(schema, key, update.$set, now); + } + } + } + + const updateKeys = Object.keys(update).filter(key => !key.startsWith('$')); + for (const key of updateKeys) { + applyTimestampsToUpdateKey(schema, key, update, now); + } +} + +function applyTimestampsToDocumentArray(arr, schematype, now) { + const timestamps = schematype.schema.options.timestamps; + + if (!timestamps) { + return; + } + + const len = arr.length; + + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + for (let i = 0; i < len; ++i) { + if (updatedAt != null) { + arr[i][updatedAt] = now; + } + if (createdAt != null) { + arr[i][createdAt] = now; + } + + applyTimestampsToChildren(now, arr[i], schematype.schema); + } +} + +function applyTimestampsToSingleNested(subdoc, schematype, now) { + const timestamps = schematype.schema.options.timestamps; + if (!timestamps) { + return; + } + + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + if (updatedAt != null) { + subdoc[updatedAt] = now; + } + if (createdAt != null) { + subdoc[createdAt] = now; + } + + applyTimestampsToChildren(now, subdoc, schematype.schema); +} + +function applyTimestampsToUpdateKey(schema, key, update, now) { + // Replace positional operator `$` and array filters `$[]` and `$[.*]` + const keyToSearch = cleanPositionalOperators(key); + const path = schema.path(keyToSearch); + if (!path) { + return; + } + + const parentSchemaTypes = []; + const pieces = keyToSearch.split('.'); + for (let i = pieces.length - 1; i > 0; --i) { + const s = schema.path(pieces.slice(0, i).join('.')); + if (s != null && + (s.$isMongooseDocumentArray || s.$isSingleNested)) { + parentSchemaTypes.push({ parentPath: key.split('.').slice(0, i).join('.'), parentSchemaType: s }); + } + } + + if (Array.isArray(update[key]) && path.$isMongooseDocumentArray) { + applyTimestampsToDocumentArray(update[key], path, now); + } else if (update[key] && path.$isSingleNested) { + applyTimestampsToSingleNested(update[key], path, now); + } else if (parentSchemaTypes.length > 0) { + for (const item of parentSchemaTypes) { + const parentPath = item.parentPath; + const parentSchemaType = item.parentSchemaType; + const timestamps = parentSchemaType.schema.options.timestamps; + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + + if (!timestamps || updatedAt == null) { + continue; + } + + if (parentSchemaType.$isSingleNested) { + // Single nested is easy + update[parentPath + '.' + updatedAt] = now; + } else if (parentSchemaType.$isMongooseDocumentArray) { + let childPath = key.substr(parentPath.length + 1); + + if (/^\d+$/.test(childPath)) { + update[parentPath + '.' + childPath][updatedAt] = now; + continue; + } + + const firstDot = childPath.indexOf('.'); + childPath = firstDot !== -1 ? childPath.substr(0, firstDot) : childPath; + + update[parentPath + '.' + childPath + '.' + updatedAt] = now; + } + } + } else if (path.schema != null && path.schema != schema && update[key]) { + const timestamps = path.schema.options.timestamps; + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + + if (!timestamps) { + return; + } + + if (updatedAt != null) { + update[key][updatedAt] = now; + } + if (createdAt != null) { + update[key][createdAt] = now; + } + } +}
\ No newline at end of file diff --git a/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js b/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js new file mode 100644 index 0000000..27d6e32 --- /dev/null +++ b/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js @@ -0,0 +1,119 @@ +'use strict'; + +/*! + * ignore + */ + +const get = require('../get'); + +module.exports = applyTimestampsToUpdate; + +/*! + * ignore + */ + +function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, options) { + const updates = currentUpdate; + let _updates = updates; + const overwrite = get(options, 'overwrite', false); + const timestamps = get(options, 'timestamps', true); + + // Support skipping timestamps at the query level, see gh-6980 + if (!timestamps || updates == null) { + return currentUpdate; + } + + const skipCreatedAt = timestamps != null && timestamps.createdAt === false; + const skipUpdatedAt = timestamps != null && timestamps.updatedAt === false; + + if (overwrite) { + if (currentUpdate && currentUpdate.$set) { + currentUpdate = currentUpdate.$set; + updates.$set = {}; + _updates = updates.$set; + } + if (!skipUpdatedAt && updatedAt && !currentUpdate[updatedAt]) { + _updates[updatedAt] = now; + } + if (!skipCreatedAt && createdAt && !currentUpdate[createdAt]) { + _updates[createdAt] = now; + } + return updates; + } + currentUpdate = currentUpdate || {}; + + if (Array.isArray(updates)) { + // Update with aggregation pipeline + updates.push({ $set: { updatedAt: now } }); + + return updates; + } + + updates.$set = updates.$set || {}; + if (!skipUpdatedAt && updatedAt && + (!currentUpdate.$currentDate || !currentUpdate.$currentDate[updatedAt])) { + let timestampSet = false; + if (updatedAt.indexOf('.') !== -1) { + const pieces = updatedAt.split('.'); + for (let i = 1; i < pieces.length; ++i) { + const remnant = pieces.slice(-i).join('.'); + const start = pieces.slice(0, -i).join('.'); + if (currentUpdate[start] != null) { + currentUpdate[start][remnant] = now; + timestampSet = true; + break; + } else if (currentUpdate.$set && currentUpdate.$set[start]) { + currentUpdate.$set[start][remnant] = now; + timestampSet = true; + break; + } + } + } + + if (!timestampSet) { + updates.$set[updatedAt] = now; + } + + if (updates.hasOwnProperty(updatedAt)) { + delete updates[updatedAt]; + } + } + + if (!skipCreatedAt && createdAt) { + if (currentUpdate[createdAt]) { + delete currentUpdate[createdAt]; + } + if (currentUpdate.$set && currentUpdate.$set[createdAt]) { + delete currentUpdate.$set[createdAt]; + } + + let timestampSet = false; + if (createdAt.indexOf('.') !== -1) { + const pieces = createdAt.split('.'); + for (let i = 1; i < pieces.length; ++i) { + const remnant = pieces.slice(-i).join('.'); + const start = pieces.slice(0, -i).join('.'); + if (currentUpdate[start] != null) { + currentUpdate[start][remnant] = now; + timestampSet = true; + break; + } else if (currentUpdate.$set && currentUpdate.$set[start]) { + currentUpdate.$set[start][remnant] = now; + timestampSet = true; + break; + } + } + } + + if (!timestampSet) { + updates.$setOnInsert = updates.$setOnInsert || {}; + updates.$setOnInsert[createdAt] = now; + } + } + + if (Object.keys(updates.$set).length === 0) { + delete updates.$set; + } + + return updates; +} diff --git a/node_modules/mongoose/lib/helpers/update/castArrayFilters.js b/node_modules/mongoose/lib/helpers/update/castArrayFilters.js new file mode 100644 index 0000000..57018d9 --- /dev/null +++ b/node_modules/mongoose/lib/helpers/update/castArrayFilters.js @@ -0,0 +1,78 @@ +'use strict'; + +const castFilterPath = require('../query/castFilterPath'); +const cleanPositionalOperators = require('../schema/cleanPositionalOperators'); +const getPath = require('../schema/getPath'); +const modifiedPaths = require('./modifiedPaths'); + +module.exports = function castArrayFilters(query) { + const arrayFilters = query.options.arrayFilters; + if (!Array.isArray(arrayFilters)) { + return; + } + + const update = query.getUpdate(); + const schema = query.schema; + const strictQuery = schema.options.strictQuery; + + const updatedPaths = modifiedPaths(update); + + const updatedPathsByFilter = Object.keys(updatedPaths).reduce((cur, path) => { + const matches = path.match(/\$\[[^\]]+\]/g); + if (matches == null) { + return cur; + } + for (const match of matches) { + const firstMatch = path.indexOf(match); + if (firstMatch !== path.lastIndexOf(match)) { + throw new Error(`Path '${path}' contains the same array filter multiple times`); + } + cur[match.substring(2, match.length - 1)] = path. + substr(0, firstMatch - 1). + replace(/\$\[[^\]]+\]/g, '0'); + } + return cur; + }, {}); + + for (const filter of arrayFilters) { + if (filter == null) { + throw new Error(`Got null array filter in ${arrayFilters}`); + } + for (const key in filter) { + + if (filter[key] == null) { + continue; + } + + const dot = key.indexOf('.'); + let filterPath = dot === -1 ? + updatedPathsByFilter[key] + '.0' : + updatedPathsByFilter[key.substr(0, dot)] + '.0' + key.substr(dot); + + if (filterPath == null) { + throw new Error(`Filter path not found for ${key}`); + } + + // If there are multiple array filters in the path being updated, make sure + // to replace them so we can get the schema path. + filterPath = cleanPositionalOperators(filterPath); + + const schematype = getPath(schema, filterPath); + if (schematype == null) { + if (!strictQuery) { + return; + } + // For now, treat `strictQuery = true` and `strictQuery = 'throw'` as + // equivalent for casting array filters. `strictQuery = true` doesn't + // quite work in this context because we never want to silently strip out + // array filters, even if the path isn't in the schema. + throw new Error(`Could not find path "${filterPath}" in schema`); + } + if (typeof filter[key] === 'object') { + filter[key] = castFilterPath(query, schematype, filter[key]); + } else { + filter[key] = schematype.castForQuery(filter[key]); + } + } + } +};
\ No newline at end of file diff --git a/node_modules/mongoose/lib/helpers/update/modifiedPaths.js b/node_modules/mongoose/lib/helpers/update/modifiedPaths.js new file mode 100644 index 0000000..9cb567d --- /dev/null +++ b/node_modules/mongoose/lib/helpers/update/modifiedPaths.js @@ -0,0 +1,33 @@ +'use strict'; + +const _modifiedPaths = require('../common').modifiedPaths; + +/** + * Given an update document with potential update operators (`$set`, etc.) + * returns an object whose keys are the directly modified paths. + * + * If there are any top-level keys that don't start with `$`, we assume those + * will get wrapped in a `$set`. The Mongoose Query is responsible for wrapping + * top-level keys in `$set`. + * + * @param {Object} update + * @return {Object} modified + */ + +module.exports = function modifiedPaths(update) { + const keys = Object.keys(update); + const res = {}; + + const withoutDollarKeys = {}; + for (const key of keys) { + if (key.startsWith('$')) { + _modifiedPaths(update[key], '', res); + continue; + } + withoutDollarKeys[key] = update[key]; + } + + _modifiedPaths(withoutDollarKeys, '', res); + + return res; +}; diff --git a/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js b/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js new file mode 100644 index 0000000..8541c5b --- /dev/null +++ b/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js @@ -0,0 +1,53 @@ +'use strict'; + +const get = require('../get'); + +/** + * Given an update, move all $set on immutable properties to $setOnInsert. + * This should only be called for upserts, because $setOnInsert bypasses the + * strictness check for immutable properties. + */ + +module.exports = function moveImmutableProperties(schema, update, ctx) { + if (update == null) { + return; + } + + const keys = Object.keys(update); + for (const key of keys) { + const isDollarKey = key.startsWith('$'); + + if (key === '$set') { + const updatedPaths = Object.keys(update[key]); + for (const path of updatedPaths) { + _walkUpdatePath(schema, update[key], path, update, ctx); + } + } else if (!isDollarKey) { + _walkUpdatePath(schema, update, key, update, ctx); + } + + } +}; + +function _walkUpdatePath(schema, op, path, update, ctx) { + const schematype = schema.path(path); + if (schematype == null) { + return; + } + + let immutable = get(schematype, 'options.immutable', null); + if (immutable == null) { + return; + } + if (typeof immutable === 'function') { + immutable = immutable.call(ctx, ctx); + } + + if (!immutable) { + return; + } + + update.$setOnInsert = update.$setOnInsert || {}; + update.$setOnInsert[path] = op[path]; + delete op[path]; +}
\ No newline at end of file diff --git a/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js b/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js new file mode 100644 index 0000000..d460628 --- /dev/null +++ b/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js @@ -0,0 +1,18 @@ +'use strict'; + +/** + * MongoDB throws an error if there's unused array filters. That is, if `options.arrayFilters` defines + * a filter, but none of the `update` keys use it. This should be enough to filter out all unused array + * filters. + */ + +module.exports = function removeUnusedArrayFilters(update, arrayFilters) { + const updateKeys = Object.keys(update).map(key => Object.keys(update[key])).reduce((cur, arr) => cur.concat(arr), []); + return arrayFilters.filter(obj => { + const firstKey = Object.keys(obj)[0]; + const firstDot = firstKey.indexOf('.'); + const arrayFilterKey = firstDot === -1 ? firstKey : firstKey.slice(0, firstDot); + + return updateKeys.find(key => key.includes('$[' + arrayFilterKey + ']')) != null; + }); +};
\ No newline at end of file |