From 3893b749f1533b1776b06e43008609677f5f25fa Mon Sep 17 00:00:00 2001 From: hassan-badat Date: Tue, 16 Apr 2024 20:02:22 -0400 Subject: [PATCH] chore: resolve codeQL vulnerabilities --- lib/mongodb.js | 687 +++++++++++++++++++++++++++---------------------- 1 file changed, 383 insertions(+), 304 deletions(-) diff --git a/lib/mongodb.js b/lib/mongodb.js index cb647c3dc..2d3891abb 100644 --- a/lib/mongodb.js +++ b/lib/mongodb.js @@ -113,7 +113,7 @@ exports.initialize = function initializeDataSource(dataSource, callback) { if (callback) { if (s.lazyConnect) { - process.nextTick(function() { + process.nextTick(function () { callback(); }); } else { @@ -162,9 +162,9 @@ exports.fieldsArrayToObj = fieldsArrayToObj; */ function fieldsArrayToObj(fieldsArray) { if (!Array.isArray(fieldsArray)) return fieldsArray; // fail safe check in case non array object created - return fieldsArray.length ? - fieldsArray.reduce(arrayToObjectReducer, {}) : - {_id: 1}; + return fieldsArray.length + ? fieldsArray.reduce(arrayToObjectReducer, {}) + : { _id: 1 }; } exports.MongoDB = MongoDB; @@ -211,15 +211,15 @@ util.inherits(MongoDB, Connector); * @param {Error} err The error object * @param {Db} db The mongo DB object */ -MongoDB.prototype.connect = function(callback) { +MongoDB.prototype.connect = function (callback) { const self = this; if (self.db) { - process.nextTick(function() { + process.nextTick(function () { if (callback) callback(null, self.db); }); } else if (self.dataSource.connecting) { - self.dataSource.once('connected', function() { - process.nextTick(function() { + self.dataSource.once('connected', function () { + process.nextTick(function () { if (callback) callback(null, self.db); }); }); @@ -297,7 +297,7 @@ MongoDB.prototype.connect = function(callback) { const lbOptions = Object.keys(self.settings); const validOptions = {}; - lbOptions.forEach(function(option) { + lbOptions.forEach(function (option) { if (validOptionNames.indexOf(option) > -1) { validOptions[option] = self.settings[option]; } @@ -310,15 +310,15 @@ MongoDB.prototype.connect = function(callback) { g.error( '{{MongoDB}} connection is failed: %s %s', self.settings.url, - err, + err ); } if (callback) callback(err); } - new mongodb.MongoClient(self.settings.url, validOptions).connect(function( + new mongodb.MongoClient(self.settings.url, validOptions).connect(function ( err, - client, + client ) { if (err) { onError(err); @@ -329,14 +329,14 @@ MongoDB.prototype.connect = function(callback) { } self.client = client; // The database name might be in the url - return urlParser(self.settings.url, self.settings, function(err, url) { + return urlParser(self.settings.url, self.settings, function (err, url) { if (err) { onError(err); return; } self.db = client.db( url.dbName || self.settings.database, - url.db_options || self.settings, + url.db_options || self.settings ); if (callback) callback(err, self.db); }); @@ -344,11 +344,11 @@ MongoDB.prototype.connect = function(callback) { } }; -MongoDB.prototype.getTypes = function() { +MongoDB.prototype.getTypes = function () { return ['db', 'nosql', 'mongodb']; }; -MongoDB.prototype.getDefaultIdType = function() { +MongoDB.prototype.getDefaultIdType = function () { return ObjectID; }; @@ -357,7 +357,7 @@ MongoDB.prototype.getDefaultIdType = function() { * @param {String} modelName The model name * @returns {String} collection name */ -MongoDB.prototype.collectionName = function(modelName) { +MongoDB.prototype.collectionName = function (modelName) { const modelClass = this._models[modelName]; if (modelClass.settings.mongodb) { modelName = modelClass.settings.mongodb.collection || modelName; @@ -370,7 +370,7 @@ MongoDB.prototype.collectionName = function(modelName) { * @param {String} modelName The model name * @returns {*} */ -MongoDB.prototype.collection = function(modelName) { +MongoDB.prototype.collection = function (modelName) { if (!this.db) { throw new Error(g.f('{{MongoDB}} connection is not established')); } @@ -384,11 +384,13 @@ MongoDB.prototype.collection = function(modelName) { * @param {String} modelName The model name * @param {Object} data The data from DB */ -MongoDB.prototype.fromDatabase = function(modelName, data) { +MongoDB.prototype.fromDatabase = function (modelName, data) { if (!data) { return null; } - const modelInfo = this._models[modelName] || this.dataSource.modelBuilder.definitions[modelName]; + const modelInfo = + this._models[modelName] || + this.dataSource.modelBuilder.definitions[modelName]; const props = modelInfo.properties; for (const p in props) { const prop = props[p]; @@ -429,7 +431,7 @@ MongoDB.prototype.fromDatabase = function(modelName, data) { * @param {String} modelName The model name * @param {Object} data The JSON data to convert */ -MongoDB.prototype.toDatabase = function(modelName, data) { +MongoDB.prototype.toDatabase = function (modelName, data) { const modelCtor = this._models[modelName]; const props = modelCtor.properties; @@ -442,7 +444,8 @@ MongoDB.prototype.toDatabase = function(modelName, data) { for (const p in props) { const prop = props[p]; - const isGeoPoint = data[p] && prop && prop.type && prop.type.name === 'GeoPoint'; + const isGeoPoint = + data[p] && prop && prop.type && prop.type.name === 'GeoPoint'; if (isGeoPoint) { data[p] = { coordinates: [data[p].lng, data[p].lat], @@ -464,7 +467,7 @@ MongoDB.prototype.toDatabase = function(modelName, data) { * @param {String} command The command name * @param [...] params Parameters for the given command */ -MongoDB.prototype.execute = function(modelName, command) { +MongoDB.prototype.execute = function (modelName, command) { const self = this; // Get the parameters for the given command const args = [].slice.call(arguments, 2); @@ -480,13 +483,13 @@ MongoDB.prototype.execute = function(modelName, command) { self.disconnect(); self.db = null; } - self.connect(function(err, db) { + self.connect(function (err, db) { if (err) { debug( 'Connection not established - MongoDB: model=%s command=%s -- error=%s', modelName, command, - err, + err ); } doExecute(); @@ -495,14 +498,17 @@ MongoDB.prototype.execute = function(modelName, command) { function doExecute() { let collection; - const context = Object.assign({}, { - model: modelName, - collection: collection, - req: { - command: command, - params: args, - }, - }); + const context = Object.assign( + {}, + { + model: modelName, + collection: collection, + req: { + command: command, + params: args, + }, + } + ); try { collection = self.collection(modelName); @@ -519,8 +525,8 @@ MongoDB.prototype.execute = function(modelName, command) { self.notifyObserversAround( 'execute', context, - function(context, done) { - args[args.length - 1] = function(err, result) { + function (context, done) { + args[args.length - 1] = function (err, result) { if (err) { debug('Error: ', err); } else { @@ -532,12 +538,12 @@ MongoDB.prototype.execute = function(modelName, command) { debug('MongoDB: model=%s command=%s', modelName, command, args); return collection[command].apply(collection, args); }, - callback, + callback ); } }; -MongoDB.prototype.coerceId = function(modelName, id, options) { +MongoDB.prototype.coerceId = function (modelName, id, options) { // See https://github.com/strongloop/loopback-connector-mongodb/issues/206 if (id == null) return id; const self = this; @@ -568,7 +574,7 @@ MongoDB.prototype.coerceId = function(modelName, id, options) { * @param {Object} data The model data * @param {Function} [callback] The callback function */ -MongoDB.prototype.create = function(modelName, data, options, callback) { +MongoDB.prototype.create = function (modelName, data, options, callback) { const self = this; if (self.debug) { debug('create', modelName, data); @@ -587,31 +593,36 @@ MongoDB.prototype.create = function(modelName, data, options, callback) { } data = self.toDatabase(modelName, data); - this.execute(modelName, 'insertOne', data, addOption({safe: true}, - options, 'checkKeys'), function(err, result) { - if (self.debug) { - debug('create.callback', modelName, err, result); - } - if (err) { - return callback(err); - } - idValue = result.ops[0]._id; + this.execute( + modelName, + 'insertOne', + data, + addOption({ safe: true }, options, 'checkKeys'), + function (err, result) { + if (self.debug) { + debug('create.callback', modelName, err, result); + } + if (err) { + return callback(err); + } + idValue = result.ops[0]._id; - try { - idValue = self.coerceId(modelName, idValue, options); - } catch (err) { - return callback(err); - } + try { + idValue = self.coerceId(modelName, idValue, options); + } catch (err) { + return callback(err); + } - // Wrap it to process.nextTick as delete data._id seems to be interferring - // with mongo insert - process.nextTick(function() { - // Restore the data object - delete data._id; - data[idName] = idValue; - callback(err, err ? null : idValue); - }); - }); + // Wrap it to process.nextTick as delete data._id seems to be interferring + // with mongo insert + process.nextTick(function () { + // Restore the data object + delete data._id; + data[idName] = idValue; + callback(err, err ? null : idValue); + }); + } + ); }; /** @@ -620,7 +631,7 @@ MongoDB.prototype.create = function(modelName, data, options, callback) { * @param {Object} data The model data * @param {Function} [callback] The callback function */ -MongoDB.prototype.save = function(modelName, data, options, callback) { +MongoDB.prototype.save = function (modelName, data, options, callback) { const self = this; if (self.debug) { debug('save', modelName, data); @@ -636,38 +647,44 @@ MongoDB.prototype.save = function(modelName, data, options, callback) { data = self.toDatabase(modelName, data); - this.execute(modelName, 'updateOne', {_id: oid}, {$set: data}, addOption({upsert: true}, - options, 'checkKeys'), function(err, result) { - if (!err) { - self.setIdValue(modelName, data, idValue); - if (idName !== '_id') { - delete data._id; + this.execute( + modelName, + 'updateOne', + { _id: oid }, + { $set: data }, + addOption({ upsert: true }, options, 'checkKeys'), + function (err, result) { + if (!err) { + self.setIdValue(modelName, data, idValue); + if (idName !== '_id') { + delete data._id; + } } - } - if (self.debug) { - debug('save.callback', modelName, err, result); - } - - const info = {}; - if (result && result.result) { - // create result formats: - // { ok: 1, n: 1, upserted: [ [Object] ] } - // { ok: 1, nModified: 0, n: 1, upserted: [ [Object] ] } - // - // update result formats: - // { ok: 1, n: 1 } - // { ok: 1, nModified: 1, n: 1 } - if (result.result.ok === 1 && result.result.n === 1) { - info.isNewInstance = !!result.result.upserted; - } else { - debug('save result format not recognized: %j', result.result); + if (self.debug) { + debug('save.callback', modelName, err, result); } - } - if (callback) { - callback(err, result && result.ops, info); + const info = {}; + if (result && result.result) { + // create result formats: + // { ok: 1, n: 1, upserted: [ [Object] ] } + // { ok: 1, nModified: 0, n: 1, upserted: [ [Object] ] } + // + // update result formats: + // { ok: 1, n: 1 } + // { ok: 1, nModified: 1, n: 1 } + if (result.result.ok === 1 && result.result.n === 1) { + info.isNewInstance = !!result.result.upserted; + } else { + debug('save result format not recognized: %j', result.result); + } + } + + if (callback) { + callback(err, result && result.ops, info); + } } - }); + ); }; /** @@ -677,13 +694,13 @@ MongoDB.prototype.save = function(modelName, data, options, callback) { * @param {Function} [callback] The callback function * */ -MongoDB.prototype.exists = function(modelName, id, options, callback) { +MongoDB.prototype.exists = function (modelName, id, options, callback) { const self = this; if (self.debug) { debug('exists', modelName, id); } id = self.coerceId(modelName, id, options); - this.execute(modelName, 'findOne', {_id: id}, function(err, data) { + this.execute(modelName, 'findOne', { _id: id }, function (err, data) { if (self.debug) { debug('exists.callback', modelName, id, err, data); } @@ -704,7 +721,7 @@ MongoDB.prototype.find = function find(modelName, id, options, callback) { } const idName = self.idName(modelName); const oid = self.coerceId(modelName, id, options); - this.execute(modelName, 'findOne', {_id: oid}, function(err, data) { + this.execute(modelName, 'findOne', { _id: oid }, function (err, data) { if (self.debug) { debug('find.callback', modelName, id, err, data); } @@ -728,7 +745,7 @@ Connector.defineAliases(MongoDB.prototype, 'find', 'findById'); * @param data * @returns {*} */ -MongoDB.prototype.parseUpdateData = function(modelName, data, options) { +MongoDB.prototype.parseUpdateData = function (modelName, data, options) { options = options || {}; const parsedData = {}; @@ -804,7 +821,7 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate( modelName, data, options, - callback, + callback ) { const self = this; if (self.debug) { @@ -828,12 +845,16 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate( _id: oid, }, data, - addOption({ - upsert: true, - returnOriginal: false, - sort: [['_id', 'asc']], - }, options, 'checkKeys'), - function(err, result) { + addOption( + { + upsert: true, + returnOriginal: false, + sort: [['_id', 'asc']], + }, + options, + 'checkKeys' + ), + function (err, result) { if (self.debug) { debug('updateOrCreate.callback', modelName, id, err, result); } @@ -851,7 +872,7 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate( let info; if (result && result.lastErrorObject) { - info = {isNewInstance: !result.lastErrorObject.updatedExisting}; + info = { isNewInstance: !result.lastErrorObject.updatedExisting }; } else { debug('updateOrCreate result format not recognized: %j', result); } @@ -859,7 +880,7 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate( if (callback) { callback(err, self.fromDatabase(modelName, object), info); } - }, + } ); }; @@ -871,7 +892,7 @@ MongoDB.prototype.updateOrCreate = function updateOrCreate( * @param {Object} options The options object * @param {Function} [cb] The callback function */ -MongoDB.prototype.replaceOrCreate = function(modelName, data, options, cb) { +MongoDB.prototype.replaceOrCreate = function (modelName, data, options, cb) { if (this.debug) debug('replaceOrCreate', modelName, data); const id = this.getIdValue(modelName, data); @@ -879,7 +900,7 @@ MongoDB.prototype.replaceOrCreate = function(modelName, data, options, cb) { const idName = this.idName(modelName); data._id = data[idName]; delete data[idName]; - this.replaceWithOptions(modelName, oid, data, {upsert: true}, cb); + this.replaceWithOptions(modelName, oid, data, { upsert: true }, cb); }; /** @@ -894,13 +915,13 @@ MongoDB.prototype.destroy = function destroy(modelName, id, options, callback) { debug('delete', modelName, id); } id = self.coerceId(modelName, id, options); - this.execute(modelName, 'deleteOne', {_id: id}, function(err, result) { + this.execute(modelName, 'deleteOne', { _id: id }, function (err, result) { if (self.debug) { debug('delete.callback', modelName, id, err, result); } let res = result && result.result; if (res) { - res = {count: res.n}; + res = { count: res.n }; } if (callback) { callback(err, res); @@ -935,7 +956,7 @@ function idIncluded(fields, idName) { return true; } -MongoDB.prototype.buildWhere = function(modelName, where, options) { +MongoDB.prototype.buildWhere = function (modelName, where, options) { const self = this; const query = {}; if (where === null || typeof where !== 'object') { @@ -950,11 +971,11 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) { } const idName = self.idName(modelName); - Object.keys(where).forEach(function(k) { + Object.keys(where).forEach(function (k) { let cond = where[k]; if (k === 'and' || k === 'or' || k === 'nor') { if (Array.isArray(cond)) { - cond = cond.map(function(c) { + cond = cond.map(function (c) { return self.buildWhere(modelName, c, options); }); } @@ -971,10 +992,18 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) { } const propDef = self.getPropertyDefinition(modelName, propName); - if (propDef && propDef.mongodb && typeof propDef.mongodb.dataType === 'string') { + if ( + propDef && + propDef.mongodb && + typeof propDef.mongodb.dataType === 'string' + ) { if (Decimal128TypeRegex.test(propDef.mongodb.dataType)) { cond = Decimal128.fromString(cond); - debug('buildWhere decimal value: %s, constructor name: %s', cond, cond.constructor.name); + debug( + 'buildWhere decimal value: %s, constructor name: %s', + cond, + cond.constructor.name + ); } else if (isStoredAsObjectID(propDef)) { cond = ObjectID(cond); } @@ -995,11 +1024,11 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) { if (spec) { if (spec === 'between') { - query[k] = {$gte: cond[0], $lte: cond[1]}; + query[k] = { $gte: cond[0], $lte: cond[1] }; } else if (spec === 'inq') { cond = [].concat(cond || []); query[k] = { - $in: cond.map(function(x) { + $in: cond.map(function (x) { if (isObjectIDProperty(modelCtor, propDef, x, options)) return ObjectID(x); return x; @@ -1008,7 +1037,7 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) { } else if (spec === 'nin') { cond = [].concat(cond || []); query[k] = { - $nin: cond.map(function(x) { + $nin: cond.map(function (x) { if (isObjectIDProperty(modelCtor, propDef, x, options)) return ObjectID(x); return x; @@ -1016,23 +1045,23 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) { }; } else if (spec === 'like') { if (cond instanceof RegExp) { - query[k] = {$regex: cond}; + query[k] = { $regex: cond }; } else { - query[k] = {$regex: new RegExp(cond, regexOptions)}; + query[k] = { $regex: new RegExp(cond, regexOptions) }; } } else if (spec === 'nlike') { if (cond instanceof RegExp) { - query[k] = {$not: cond}; + query[k] = { $not: cond }; } else { - query[k] = {$not: new RegExp(cond, regexOptions)}; + query[k] = { $not: new RegExp(cond, regexOptions) }; } } else if (spec === 'neq') { - query[k] = {$ne: cond}; + query[k] = { $ne: cond }; } else if (spec === 'regexp') { if (cond.global) g.warn('{{MongoDB}} regex syntax does not respect the {{`g`}} flag'); - query[k] = {$regex: cond}; + query[k] = { $regex: cond }; } else { query[k] = {}; query[k]['$' + spec] = cond; @@ -1041,7 +1070,7 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) { if (cond === null && !implicitNullType) { // http://docs.mongodb.org/manual/reference/operator/query/type/ // Null: 10 - query[k] = {$type: 10}; + query[k] = { $type: 10 }; } else { if (isObjectIDProperty(modelCtor, propDef, cond, options)) { cond = ObjectID(cond); @@ -1053,22 +1082,15 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) { return query; }; -MongoDB.prototype.buildSort = function(modelName, order, options) { +MongoDB.prototype.buildSort = function (modelName, order, options) { let sort = {}; const idName = this.idName(modelName); const modelClass = this._models[modelName]; - - let disableDefaultSort = false; - if (this.settings.hasOwnProperty('disableDefaultSort')) { - disableDefaultSort = this.settings.disableDefaultSort; - } - if (modelClass.settings.hasOwnProperty('disableDefaultSort')) { - disableDefaultSort = modelClass.settings.disableDefaultSort; - } - if (options && options.hasOwnProperty('disableDefaultSort')) { - disableDefaultSort = options.disableDefaultSort; - } + let disableDefaultSort = + this.settings.disableDefaultSort || + modelClass.settings.disableDefaultSort || + (options && options.disableDefaultSort); if (!order && !disableDefaultSort) { const idNames = this.idNames(modelName); @@ -1076,32 +1098,34 @@ MongoDB.prototype.buildSort = function(modelName, order, options) { order = idNames; } } + if (order) { order = sanitizeFilter(order, options); - let keys = order; - if (typeof keys === 'string') { - keys = keys.split(','); - } - for (let index = 0, len = keys.length; index < len; index++) { - const m = keys[index].match(/\s+(A|DE)SC$/); - let key = keys[index]; - key = key.replace(/\s+(A|DE)SC$/, '').trim(); + let keys = typeof order === 'string' ? order.split(',') : order; + + keys = keys.map((key) => key.trim()); + + keys.forEach((key) => { + let direction = 1; + if (key.endsWith('DESC')) { + direction = -1; + key = key.substring(0, key.length - 4).trim(); + } else if (key.endsWith('ASC')) { + key = key.substring(0, key.length - 3).trim(); + } + if (key === idName) { key = '_id'; } else { key = this.getDatabaseColumnName(modelName, key); } - if (m && m[1] === 'DE') { - sort[key] = -1; - } else { - sort[key] = 1; - } - } + sort[key] = direction; + }); } else if (!disableDefaultSort) { - // order by _id by default - sort = {_id: 1}; + sort = { _id: 1 }; // order by _id by default } + return sort; }; @@ -1118,8 +1142,8 @@ function convertToMeters(distance, unit) { default: console.warn( 'unsupported unit ' + - unit + - ", fallback to mongodb default unit 'meters'", + unit + + ", fallback to mongodb default unit 'meters'" ); return distance; } @@ -1130,7 +1154,7 @@ function buildNearFilter(query, params) { params = [params]; } - params.forEach(function(near) { + params.forEach(function (near) { let coordinates = {}; if (typeof near.near === 'string') { @@ -1145,7 +1169,6 @@ function buildNearFilter(query, params) { } const props = ['maxDistance', 'minDistance']; - // use mongodb default unit 'meters' rather than 'miles' const unit = near.unit || 'meters'; const queryValue = { @@ -1157,28 +1180,27 @@ function buildNearFilter(query, params) { }, }; - props.forEach(function(p) { + props.forEach(function (p) { if (near[p]) { queryValue.near['$' + p] = convertToMeters(near[p], unit); } }); - let property; + let property = Object.create(null); if (near.mongoKey) { - // if mongoKey is an Array, set the $near query at the right depth, following the Array if (Array.isArray(near.mongoKey)) { - property = query.where; + property = query.where || Object.create(null); let i; for (i = 0; i < near.mongoKey.length; i++) { const subKey = near.mongoKey[i]; if (near.mongoKey.hasOwnProperty(i + 1)) { - if (!property.hasOwnProperty(subKey)) { - property[subKey] = Number.isInteger(near.mongoKey[i + 1]) ? - [] : - {}; + if (!Object.prototype.hasOwnProperty.call(property, subKey)) { + property[subKey] = Number.isInteger(near.mongoKey[i + 1]) + ? [] + : Object.create(null); } property = property[subKey]; @@ -1187,8 +1209,8 @@ function buildNearFilter(query, params) { property[near.mongoKey[i - 1]] = queryValue; } else { - // mongoKey is a single string, just set it directly - property = query.where[near.mongoKey] = queryValue; + query.where = query.where || Object.create(null); + query.where[near.mongoKey] = queryValue; } } }); @@ -1213,7 +1235,7 @@ function hasNearFilter(where) { } if (Array.isArray(node)) { - node.forEach(function(prop) { + node.forEach(function (prop) { isFound = found(prop); if (!isFound) { @@ -1221,7 +1243,7 @@ function hasNearFilter(where) { } }); } else if (typeof node === 'object') { - Object.keys(node).forEach(function(key) { + Object.keys(node).forEach(function (key) { const prop = node[key]; isFound = found(prop); @@ -1235,7 +1257,7 @@ function hasNearFilter(where) { return isFound; } -MongoDB.prototype.getDatabaseColumnName = function(model, propName) { +MongoDB.prototype.getDatabaseColumnName = function (model, propName) { if (typeof model === 'string') { model = this._models[model]; } @@ -1253,17 +1275,19 @@ MongoDB.prototype.getDatabaseColumnName = function(model, propName) { // mongoDB connector doesn't support custom id property field name if (prop.id) { // throws if a custom field name for id is set - const customFieldName = (prop.mongodb && ( - prop.mongodb.fieldName || - prop.mongodb.field || - prop.mongodb.columnName || - prop.mongodb.column) - ) || + const customFieldName = + (prop.mongodb && + (prop.mongodb.fieldName || + prop.mongodb.field || + prop.mongodb.columnName || + prop.mongodb.column)) || prop.columnName || prop.column; if (customFieldName) { - throw new Error(`custom id field name '${customFieldName}' is not allowed` + - ` in model ${model.model.definition.name}`); + throw new Error( + `custom id field name '${customFieldName}' is not allowed` + + ` in model ${model.model.definition.name}` + ); } return propName; } else { @@ -1286,7 +1310,7 @@ MongoDB.prototype.getDatabaseColumnName = function(model, propName) { return propName; }; -MongoDB.prototype.convertColumnNames = function(model, data, direction) { +MongoDB.prototype.convertColumnNames = function (model, data, direction) { if (typeof data !== 'object') { return data; // skip } @@ -1325,11 +1349,11 @@ MongoDB.prototype.convertColumnNames = function(model, data, direction) { return data; }; -MongoDB.prototype.fromPropertyToDatabaseNames = function(model, data) { +MongoDB.prototype.fromPropertyToDatabaseNames = function (model, data) { return this.convertColumnNames(model, data, 'database'); }; -MongoDB.prototype.fromDatabaseToPropertyNames = function(model, data) { +MongoDB.prototype.fromDatabaseToPropertyNames = function (model, data) { return this.convertColumnNames(model, data, 'property'); }; @@ -1357,7 +1381,7 @@ MongoDB.prototype.all = function all(modelName, filter, options, callback) { fields = self.fromPropertyToDatabaseNames(modelName, fields); if (fields) { - const findOpts = {projection: fieldsArrayToObj(fields)}; + const findOpts = { projection: fieldsArrayToObj(fields) }; this.execute(modelName, 'find', query, findOpts, processResponse); } else { this.execute(modelName, 'find', query, processResponse); @@ -1391,14 +1415,14 @@ MongoDB.prototype.all = function all(modelName, filter, options, callback) { const shouldSetIdValue = idIncluded(fields, idName); const deleteMongoId = !shouldSetIdValue || idName !== '_id'; - cursor.toArray(function(err, data) { + cursor.toArray(function (err, data) { if (self.debug) { debug('all', modelName, filter, err, data); } if (err) { return callback(err); } - const objs = data.map(function(o) { + const objs = data.map(function (o) { if (shouldSetIdValue) { self.setIdValue(modelName, o, o._id); } @@ -1415,7 +1439,7 @@ MongoDB.prototype.all = function all(modelName, filter, options, callback) { objs, filter.include, options, - callback, + callback ); } else { callback(null, objs); @@ -1434,7 +1458,7 @@ MongoDB.prototype.destroyAll = function destroyAll( modelName, where, options, - callback, + callback ) { const self = this; if (self.debug) { @@ -1447,7 +1471,7 @@ MongoDB.prototype.destroyAll = function destroyAll( where = self.buildWhere(modelName, where, options); if (debug.enabled) debug('destroyAll where %s', util.inspect(where)); - this.execute(modelName, 'deleteMany', where || {}, function(err, info) { + this.execute(modelName, 'deleteMany', where || {}, function (err, info) { if (err) return callback && callback(err); if (self.debug) debug('destroyAll.callback', modelName, where, err, info); @@ -1455,7 +1479,7 @@ MongoDB.prototype.destroyAll = function destroyAll( const affectedCount = info.result ? info.result.n : undefined; if (callback) { - callback(err, {count: affectedCount}); + callback(err, { count: affectedCount }); } }); }; @@ -1474,8 +1498,11 @@ MongoDB.prototype.count = function count(modelName, where, options, callback) { debug('count', modelName, where); } where = self.buildWhere(modelName, where, options) || {}; - const method = Object.keys(where).length === 0 ? 'estimatedDocumentCount' : 'countDocuments'; - this.execute(modelName, method, where, function(err, count) { + const method = + Object.keys(where).length === 0 + ? 'estimatedDocumentCount' + : 'countDocuments'; + this.execute(modelName, method, where, function (err, count) { if (self.debug) { debug('count.callback', modelName, err, count); } @@ -1493,19 +1520,29 @@ MongoDB.prototype.count = function count(modelName, where, options, callback) { * @param {Object} options The options object * @param {Function} [cb] The callback function */ -MongoDB.prototype.replaceById = function replace(modelName, id, data, options, cb) { +MongoDB.prototype.replaceById = function replace( + modelName, + id, + data, + options, + cb +) { if (this.debug) debug('replace', modelName, id, data); const oid = this.coerceId(modelName, id, options); - this.replaceWithOptions(modelName, oid, data, {upsert: false}, function( - err, + this.replaceWithOptions( + modelName, + oid, data, - ) { - cb(err, data); - }); + { upsert: false }, + function (err, data) { + cb(err, data); + } + ); }; function errorIdNotFoundForReplace(idValue) { - const msg = 'Could not replace. Object with id ' + idValue + ' does not exist!'; + const msg = + 'Could not replace. Object with id ' + idValue + ' does not exist!'; const error = new Error(msg); error.statusCode = error.status = 404; return error; @@ -1519,41 +1556,58 @@ function errorIdNotFoundForReplace(idValue) { * @param {Object} options The options you want to pass for update, e.g, {upsert: true} * @callback {Function} [cb] Callback function */ -MongoDB.prototype.replaceWithOptions = function(modelName, id, data, options, cb) { +MongoDB.prototype.replaceWithOptions = function ( + modelName, + id, + data, + options, + cb +) { const self = this; const idName = self.idName(modelName); delete data[idName]; data = self.toDatabase(modelName, data); - this.execute(modelName, 'replaceOne', {_id: id}, data, options, function( - err, - info, - ) { - debug('updateWithOptions.callback', modelName, {_id: id}, data, err, info); - if (err) return cb && cb(err); - let result; - const cbInfo = {}; - if (info.result && info.result.n == 1) { - result = self.fromDatabase(modelName, data); - delete result._id; - result[idName] = id; - // create result formats: - // 2.4.x :{ ok: 1, n: 1, upserted: [ Object ] } - // { ok: 1, nModified: 0, n: 1, upserted: [ Object ] } - // - // replace result formats: - // 2.4.x: { ok: 1, n: 1 } - // { ok: 1, nModified: 1, n: 1 } - if (info.result.nModified !== undefined) { - cbInfo.isNewInstance = info.result.nModified === 0; + this.execute( + modelName, + 'replaceOne', + { _id: id }, + data, + options, + function (err, info) { + debug( + 'updateWithOptions.callback', + modelName, + { _id: id }, + data, + err, + info + ); + if (err) return cb && cb(err); + let result; + const cbInfo = {}; + if (info.result && info.result.n == 1) { + result = self.fromDatabase(modelName, data); + delete result._id; + result[idName] = id; + // create result formats: + // 2.4.x :{ ok: 1, n: 1, upserted: [ Object ] } + // { ok: 1, nModified: 0, n: 1, upserted: [ Object ] } + // + // replace result formats: + // 2.4.x: { ok: 1, n: 1 } + // { ok: 1, nModified: 1, n: 1 } + if (info.result.nModified !== undefined) { + cbInfo.isNewInstance = info.result.nModified === 0; + } + } else { + result = undefined; + err = errorIdNotFoundForReplace(id); + } + if (cb) { + cb(err, result, cbInfo); } - } else { - result = undefined; - err = errorIdNotFoundForReplace(id); - } - if (cb) { - cb(err, result, cbInfo); } - }); + ); }; /** @@ -1567,7 +1621,7 @@ MongoDB.prototype.updateAttributes = function updateAttrs( id, data, options, - cb, + cb ) { const self = this; @@ -1582,7 +1636,7 @@ MongoDB.prototype.updateAttributes = function updateAttrs( if (Object.keys(data).length === 0) { if (cb) { - process.nextTick(function() { + process.nextTick(function () { cb(null, {}); }); } @@ -1599,10 +1653,14 @@ MongoDB.prototype.updateAttributes = function updateAttrs( _id: oid, }, data, - addOption({ - sort: [['_id', 'asc']], - }, options, 'checkKeys'), - function(err, result) { + addOption( + { + sort: [['_id', 'asc']], + }, + options, + 'checkKeys' + ), + function (err, result) { if (self.debug) { debug('updateAttributes.callback', modelName, id, err, result); } @@ -1618,7 +1676,7 @@ MongoDB.prototype.updateAttributes = function updateAttrs( if (cb) { cb(err, object); } - }, + } ); }; @@ -1641,7 +1699,7 @@ MongoDB.prototype.update = MongoDB.prototype.updateAll = function updateAll( where, data, options, - cb, + cb ) { const self = this; if (self.debug) { @@ -1663,10 +1721,14 @@ MongoDB.prototype.update = MongoDB.prototype.updateAll = function updateAll( 'updateMany', where, updateData, - addOption({ - upsert: false, - }, options, 'checkKeys'), - function(err, info) { + addOption( + { + upsert: false, + }, + options, + 'checkKeys' + ), + function (err, info) { if (err) return cb && cb(err); if (self.debug) @@ -1675,16 +1737,16 @@ MongoDB.prototype.update = MongoDB.prototype.updateAll = function updateAll( const affectedCount = info.result ? info.result.n : undefined; if (cb) { - cb(err, {count: affectedCount}); + cb(err, { count: affectedCount }); } - }, + } ); }; /** * Disconnect from MongoDB */ -MongoDB.prototype.disconnect = function(cb) { +MongoDB.prototype.disconnect = function (cb) { if (this.debug) { debug('disconnect'); } @@ -1714,7 +1776,7 @@ MongoDB.prototype.disconnect = function(cb) { * present, apply to all models * @param {Function} [cb] The callback function */ -MongoDB.prototype.autoupdate = function(models, cb) { +MongoDB.prototype.autoupdate = function (models, cb) { const self = this; if (self.db) { if (self.debug) { @@ -1735,7 +1797,7 @@ MongoDB.prototype.autoupdate = function(models, cb) { async.each( models, - function(modelName, modelCallback) { + function (modelName, modelCallback) { const indexes = self._models[modelName].settings.indexes || []; let indexList = []; let index = {}; @@ -1750,7 +1812,7 @@ MongoDB.prototype.autoupdate = function(models, cb) { options.name = options.name || indexName; index.options = options; } else { - options = {name: indexName}; + options = { name: indexName }; index = { keys: index, options: options, @@ -1796,19 +1858,19 @@ MongoDB.prototype.autoupdate = function(models, cb) { properties[p].type.name === 'GeoPoint' ) { const indexType = - typeof properties[p].index === 'string' ? - properties[p].index : - '2dsphere'; + typeof properties[p].index === 'string' + ? properties[p].index + : '2dsphere'; - options = {name: 'index' + indexType + p}; + options = { name: 'index' + indexType + p }; index[p] = indexType; } else { - options = {background: true}; + options = { background: true }; if (properties[p].unique) { options.unique = true; } } - indexList.push({keys: index, options: options}); + indexList.push({ keys: index, options: options }); } } /* eslint-enable one-var */ @@ -1819,7 +1881,7 @@ MongoDB.prototype.autoupdate = function(models, cb) { async.each( indexList, - function(index, indexCallback) { + function (index, indexCallback) { if (self.debug) { debug('createIndex: ', index); } @@ -1828,16 +1890,16 @@ MongoDB.prototype.autoupdate = function(models, cb) { .createIndex( index.fields || index.keys, index.options, - indexCallback, + indexCallback ); }, - modelCallback, + modelCallback ); }, - cb, + cb ); } else { - self.dataSource.once('connected', function() { + self.dataSource.once('connected', function () { self.autoupdate(models, cb); }); } @@ -1849,7 +1911,7 @@ MongoDB.prototype.autoupdate = function(models, cb) { * @param {String[]} [models] A model name or an array of model names. If not present, apply to all models * @param {Function} [cb] The callback function */ -MongoDB.prototype.automigrate = function(models, cb) { +MongoDB.prototype.automigrate = function (models, cb) { const self = this; if (self.db) { if (self.debug) { @@ -1869,19 +1931,19 @@ MongoDB.prototype.automigrate = function(models, cb) { // Make it serial as multiple models might map to the same collection async.eachSeries( models, - function(modelName, modelCallback) { + function (modelName, modelCallback) { const collectionName = self.collectionName(modelName); if (self.debug) { debug('drop collection %s for model %s', collectionName, modelName); } - self.db.dropCollection(collectionName, function(err, collection) { + self.db.dropCollection(collectionName, function (err, collection) { if (err) { debug( 'Error dropping collection %s for model %s: ', collectionName, modelName, - err, + err ); if ( !( @@ -1896,37 +1958,41 @@ MongoDB.prototype.automigrate = function(models, cb) { } // Recreate the collection if (self.debug) { - debug('create collection %s for model %s', collectionName, modelName); + debug( + 'create collection %s for model %s', + collectionName, + modelName + ); } self.db.createCollection(collectionName, modelCallback); }); }, - function(err) { + function (err) { if (err) { return cb && cb(err); } self.autoupdate(models, cb); - }, + } ); } else { - self.dataSource.once('connected', function() { + self.dataSource.once('connected', function () { self.automigrate(models, cb); }); } }; -MongoDB.prototype.ping = function(cb) { +MongoDB.prototype.ping = function (cb) { const self = this; if (self.db) { - this.db.collection('dummy').findOne({_id: 1}, cb); + this.db.collection('dummy').findOne({ _id: 1 }, cb); } else { - self.dataSource.once('connected', function() { + self.dataSource.once('connected', function () { self.ping(cb); }); - self.dataSource.once('error', function(err) { + self.dataSource.once('error', function (err) { cb(err); }); - self.connect(function() {}); + self.connect(function () {}); } }; @@ -1943,7 +2009,8 @@ function isStoredAsObjectID(propDef) { if (propDef.mongodb) { if (ObjectIdTypeRegex.test(propDef.mongodb.dataType)) return true; } else if (propDef.type) { - if (typeof propDef.type === 'string' && typeIsObjectId(propDef.type)) return true; + if (typeof propDef.type === 'string' && typeIsObjectId(propDef.type)) + return true; else if (Array.isArray(propDef.type)) { if (propDef.type[0] === ObjectID || typeIsObjectId(propDef.type[0])) { return true; @@ -1956,10 +2023,12 @@ function isStoredAsObjectID(propDef) { // Determine if strictObjectIDCoercion should be enabled function isStrictObjectIDCoercionEnabled(modelCtor, options) { const settings = modelCtor.settings; - return (settings && settings.strictObjectIDCoercion) || - (modelCtor.model && modelCtor.model.getConnector().settings.strictObjectIDCoercion) || - options && - options.strictObjectIDCoercion; + return ( + (settings && settings.strictObjectIDCoercion) || + (modelCtor.model && + modelCtor.model.getConnector().settings.strictObjectIDCoercion) || + (options && options.strictObjectIDCoercion) + ); } // Tries to coerce a property into ObjectID after checking multiple conditions @@ -2063,9 +2132,9 @@ function optimizedFindOrCreate(modelName, filter, data, options, callback) { this.collection(modelName).findOneAndUpdate( query, - {$setOnInsert: data}, - {projection: projection, sort: sort, upsert: true}, - function(err, result) { + { $setOnInsert: data }, + { projection: projection, sort: sort, upsert: true }, + function (err, result) { if (self.debug) { debug('findOrCreate.callback', modelName, filter, err, result); } @@ -2089,16 +2158,17 @@ function optimizedFindOrCreate(modelName, filter, data, options, callback) { } if (filter && filter.include) { - self._models[modelName].model.include([value], filter.include, function( - err, - data, - ) { - callback(err, data[0], created); - }); + self._models[modelName].model.include( + [value], + filter.include, + function (err, data) { + callback(err, data[0], created); + } + ); } else { callback(null, value, created); } - }, + } ); } @@ -2110,7 +2180,9 @@ function optimizedFindOrCreate(modelName, filter, data, options, callback) { */ function visitAllProperties(data, modelCtor, visitor) { if (data === null || data === undefined) return; - const modelProps = modelCtor.properties ? modelCtor.properties : modelCtor.definition.properties; + const modelProps = modelCtor.properties + ? modelCtor.properties + : modelCtor.definition.properties; const allProps = new Set(Object.keys(data).concat(Object.keys(modelProps))); for (const p of allProps) { const value = data[p]; @@ -2125,7 +2197,9 @@ function visitAllProperties(data, modelCtor, visitor) { visitAllProperties(value, def.type.definition, visitor); } } else { - visitor(modelCtor, value, def, (newValue) => { data[p] = newValue; }); + visitor(modelCtor, value, def, (newValue) => { + data[p] = newValue; + }); } continue; } @@ -2144,7 +2218,7 @@ function coercePropertyValue(modelCtor, propValue, propDef, setValue) { if (typeof dataType === 'string') { if (hasDataType('decimal128', propDef)) { if (Array.isArray(propValue)) { - coercedValue = propValue.map(val => Decimal128.fromString(val)); + coercedValue = propValue.map((val) => Decimal128.fromString(val)); return setValue(coercedValue); } else { coercedValue = Decimal128.fromString(propValue); @@ -2152,9 +2226,9 @@ function coercePropertyValue(modelCtor, propValue, propDef, setValue) { } } else if (typeIsObjectId(dataType)) { if (Array.isArray(propValue)) { - propValue = propValue.map(val => { + propValue = propValue.map((val) => { if (isObjectIDProperty(modelCtor, propDef, val)) { - return coercedValue = ObjectID(propValue); + return (coercedValue = ObjectID(propValue)); } else { throw new Error(`${val} is not an ObjectID string`); } @@ -2171,7 +2245,9 @@ function coercePropertyValue(modelCtor, propValue, propDef, setValue) { } else { // Object ID coercibility depends on multiple factors, let coerceToObjectId() handle it if (Array.isArray(propValue)) { - propValue = propValue.map(val => coerceToObjectId(modelCtor, propDef, val)); + propValue = propValue.map((val) => + coerceToObjectId(modelCtor, propDef, val) + ); } else { propValue = coerceToObjectId(modelCtor, propDef, propValue); } @@ -2180,11 +2256,11 @@ function coercePropertyValue(modelCtor, propValue, propDef, setValue) { } /** -* A utility function which checks for nested property definitions -* -* @param {*} propType Property type metadata -* -*/ + * A utility function which checks for nested property definitions + * + * @param {*} propType Property type metadata + * + */ function isNestedModel(propType) { if (!propType) return false; if (Array.isArray(propType)) return isNestedModel(propType[0]); @@ -2192,23 +2268,26 @@ function isNestedModel(propType) { } /** -* A utility function which checks if a certain property definition matches -* the given data type -* @param {*} dataType The data type to check the property definition against -* @param {*} propertyDef A property definition containing metadata about property type -*/ + * A utility function which checks if a certain property definition matches + * the given data type + * @param {*} dataType The data type to check the property definition against + * @param {*} propertyDef A property definition containing metadata about property type + */ function hasDataType(dataType, propertyDef) { - return propertyDef && propertyDef.mongodb && + return ( + propertyDef && + propertyDef.mongodb && propertyDef.mongodb.dataType && - propertyDef.mongodb.dataType.toLowerCase() === dataType.toLowerCase(); + propertyDef.mongodb.dataType.toLowerCase() === dataType.toLowerCase() + ); } /** -* A utility function which adds the connector options to mongodb options -* the given data type -* @param {*} commandOptions The command options -* @param {*} connectorOptions The connector options -* @param {*} property Connector's option's property name -*/ + * A utility function which adds the connector options to mongodb options + * the given data type + * @param {*} commandOptions The command options + * @param {*} connectorOptions The connector options + * @param {*} property Connector's option's property name + */ function addOption(commandOptions, connectorOptions, property) { if (!commandOptions) { commandOptions = {};