diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f06a53a7a..83389d233 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,6 @@ jobs: strategy: matrix: node-version: - - 14.x - 16.x - 18.x steps: diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 98d7633d8..40809a1a9 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,2 +1,3 @@ -- Upgrade express de from 4.19.2 to 4.20.0 -- Upgrade mongoose dep from 5.13.20 to 5.13.22 +- Upgrade express dep from 4.19.2 to 4.20.0 +- Upgrade mongodb devdep from 4.17.1 to 4.17.2 +- Upgrade mongoose dep from 5.13.20 to 8.4.4 (solving vulnerability CVE-2024-53900) (#1674) diff --git a/lib/model/Command.js b/lib/model/Command.js index 5205ea0f0..4d15acb61 100644 --- a/lib/model/Command.js +++ b/lib/model/Command.js @@ -34,8 +34,8 @@ const Command = new Schema({ creationDate: { type: Date, default: Date.now } }); -function load(db) { - module.exports.model = db.model('Command', Command); +function load() { + module.exports.model = mongoose.model('Command', Command); module.exports.internalSchema = Command; } diff --git a/lib/model/Device.js b/lib/model/Device.js index 073211df7..a6611fe73 100644 --- a/lib/model/Device.js +++ b/lib/model/Device.js @@ -56,8 +56,8 @@ const Device = new Schema({ payloadType: String }); -function load(db) { - module.exports.model = db.model('Device', Device); +function load() { + module.exports.model = mongoose.model('Device', Device); module.exports.internalSchema = Device; } diff --git a/lib/model/Group.js b/lib/model/Group.js index 91230d7b2..cf710be26 100644 --- a/lib/model/Group.js +++ b/lib/model/Group.js @@ -68,9 +68,9 @@ const Group = new Schema({ payloadType: String }); -function load(db) { +function load() { Group.index({ apikey: 1, resource: 1 }, { unique: true }); - module.exports.model = db.model('Group', Group); + module.exports.model = mongoose.model('Group', Group); module.exports.internalSchema = Group; } diff --git a/lib/model/dbConn.js b/lib/model/dbConn.js index b58198909..a434d5605 100644 --- a/lib/model/dbConn.js +++ b/lib/model/dbConn.js @@ -31,7 +31,6 @@ const config = require('../commonConfig'); const constants = require('../constants'); const alarms = require('../services/common/alarmManagement'); const logger = require('logops'); -const async = require('async'); const errors = require('../errors'); let defaultDb; const DEFAULT_DB_NAME = 'iotagent'; @@ -40,9 +39,9 @@ const context = { }; function loadModels() { - require('./Device').load(defaultDb); - require('./Group').load(defaultDb); - require('./Command').load(defaultDb); + require('./Device').load(); + require('./Group').load(); + require('./Command').load(); } /** @@ -50,93 +49,52 @@ function loadModels() { * * @this Reference to the dbConn module itself. */ + function init(host, db, port, options, callback) { - /*jshint camelcase:false, validthis:true */ let url; let retries = 0; let lastError; - const maxRetries = - (config.getConfig().mongodb && config.getConfig().mongodb.retries) || constants.DEFAULT_MONGODB_RETRIES; + const maxRetries = config.getConfig().mongodb?.retries || constants.DEFAULT_MONGODB_RETRIES; function addPort(item) { - return item + ':' + port; - } - - function commaConcat(previous, current, currentIndex) { - if (currentIndex !== 0) { - previous += ','; - } - - previous += current; - - return previous; + return `${item}:${port}`; } url = 'mongodb://'; if (options.auth) { - url += options.auth.user + ':' + options.auth.password + '@'; + url += `${encodeURIComponent(options.auth.user)}:${encodeURIComponent(options.auth.password)}@`; } - const hosts = host.split(',').map(addPort).reduce(commaConcat, ''); - - url += hosts + '/' + db; + const hosts = host.split(',').map(addPort).join(','); + url += `${hosts}/${db}`; if (options.extraArgs) { - if (options.extraArgs instanceof Object && Object.keys(options.extraArgs).length > 0) { - url += '?'; - url += Object.entries(options.extraArgs) - .map(function ([k, v]) { - return encodeURIComponent(k) + '=' + encodeURIComponent(v); - }) - .join('&'); + const query = new URLSearchParams(options.extraArgs).toString(); + if (query) { + url += `?${query}`; } delete options.extraArgs; } - /* eslint-disable-next-line no-unused-vars */ - function createConnectionHandler(error, results) { - if (defaultDb) { - logger.info(context, 'Successfully connected to MongoDB.'); - module.exports.db = defaultDb; - loadModels(); - } else { - logger.error(context, 'MONGODB-002: Error found after [%d] attempts: %s', retries, error || lastError); - } - - callback(error); - } - - function retryCheck() { - return !defaultDb && retries < maxRetries; - } - - function connectionAttempt(url, options, callback) { - logger.info(context, 'Attempting to connect to MongoDB instance with url %j. Attempt %d', url, retries); - // FIXME: useNewUrlParser is no longer used in underlying mongodb driver 4.x - // (see https://github.com/mongodb/node-mongodb-native/blob/HEAD/etc/notes/CHANGES_4.0.0.md) - // but not sure if current mongoose version is still using mongodb 3.x internally - // probably mongodb-connectionoptions-test.js needs to be fixed if useNewUrlParser is removed at the end - options.useNewUrlParser = true; - options.useUnifiedTopology = true; - mongoose.set('useCreateIndex', true); - /* eslint-disable-next-line no-unused-vars */ - const candidateDb = mongoose.createConnection(url, options, function (error, result) { - if (error) { - logger.error(context, 'MONGODB-001: Error trying to connect to MongoDB: %s', error); - lastError = error; - } else { - defaultDb = candidateDb; + function connectionAttempt(callback) { + logger.info(context, `Attempting to connect to MongoDB at ${url}. Attempt ${retries + 1}`); + mongoose + .connect(url, options) + .then(() => { + defaultDb = mongoose.connection; + logger.info(context, 'Successfully connected to MongoDB.'); + loadModels(); defaultDb.on('error', function (error) { logger.error(context, 'Mongo Driver error: %j', error); + lastError = error; alarms.raise(constants.MONGO_ALARM, error); }); /* eslint-disable-next-line no-unused-vars */ defaultDb.on('connecting', function (error) { logger.debug(context, 'Mongo Driver connecting'); }); - defaultDb.on('connected', function () { logger.debug(context, 'Mongo Driver connected'); }); @@ -159,69 +117,52 @@ function init(host, db, port, options, callback) { defaultDb.on('close', function () { logger.debug(context, 'Mongo Driver close'); }); - } - - callback(); - }); - } - - function tryCreateConnection(callback) { - const attempt = async.apply(connectionAttempt, url, options, callback); - const seconds = - (config.getConfig().mongodb && config.getConfig().mongodb.retryTime) || - constants.DEFAULT_MONGODB_RETRY_TIME; - - retries++; - - if (retries === 1) { - logger.info(context, 'First connection attempt'); - attempt(); - } else { - logger.info(context, 'Waiting %d seconds before attempting again.', seconds); - setTimeout(attempt, seconds * 1000); - } + callback(); + }) + .catch((err) => { + logger.error(context, `MONGODB-001: Error trying to connect to MongoDB: ${err}`); + lastError = err; + retries++; + if (retries < maxRetries) { + const retryTime = config.getConfig().mongodb?.retryTime || constants.DEFAULT_MONGODB_RETRY_TIME; + logger.info(context, `Retrying in ${retryTime} seconds...`); + setTimeout(() => connectionAttempt(callback), retryTime * 1000); + } else { + logger.error( + context, + 'MONGODB-002: Error to connect found after %d attempts: %s', + retries, + lastError + ); + callback(err); + } + }); } - defaultDb = null; - async.whilst(retryCheck, tryCreateConnection, createConnectionHandler); + connectionAttempt(callback); } function configureDb(callback) { - /*jshint camelcase:false, validthis:true */ const currentConfig = config.getConfig(); - - if (currentConfig.deviceRegistry && currentConfig.deviceRegistry.type === 'mongodb') { - if (!currentConfig.mongodb || !currentConfig.mongodb.host) { + if (currentConfig.deviceRegistry?.type === 'mongodb') { + if (!currentConfig.mongodb?.host) { logger.fatal(context, 'MONGODB-003: No host found for MongoDB driver.'); callback(new errors.BadConfiguration('No host found for MongoDB driver')); } else { - let dbName = currentConfig.mongodb.db; + const dbName = currentConfig.mongodb.db || DEFAULT_DB_NAME; const port = currentConfig.mongodb.port || 27017; const options = {}; - if (!currentConfig.mongodb.db) { - dbName = DEFAULT_DB_NAME; - } - - if (currentConfig.mongodb.replicaSet) { - options.replicaSet = currentConfig.mongodb.replicaSet; - } - - if (currentConfig.mongodb.ssl) { - options.ssl = currentConfig.mongodb.ssl; - } - - if (currentConfig.mongodb.extraArgs) { - options.extraArgs = currentConfig.mongodb.extraArgs; - } + if (currentConfig.mongodb.replicaSet) options.replicaSet = currentConfig.mongodb.replicaSet; + if (currentConfig.mongodb.ssl) options.ssl = currentConfig.mongodb.ssl; + if (currentConfig.mongodb.extraArgs) options.extraArgs = currentConfig.mongodb.extraArgs; if (currentConfig.mongodb.user && currentConfig.mongodb.password) { - options.auth = {}; - options.auth.user = currentConfig.mongodb.user; - options.auth.password = currentConfig.mongodb.password; - // authSource only applies if auth is set + options.auth = { + user: currentConfig.mongodb.user, + password: currentConfig.mongodb.password + }; if (currentConfig.mongodb.authSource) { - // Overload extraArgs if it was set options.extraArgs = { ...options.extraArgs, authSource: currentConfig.mongodb.authSource @@ -229,7 +170,7 @@ function configureDb(callback) { } } - init(config.getConfig().mongodb.host, dbName, port, options, callback); + init(currentConfig.mongodb.host, dbName, port, options, callback); } } else { callback(); diff --git a/lib/services/commands/commandRegistryMongoDB.js b/lib/services/commands/commandRegistryMongoDB.js index a3df434ca..0021c93d1 100644 --- a/lib/services/commands/commandRegistryMongoDB.js +++ b/lib/services/commands/commandRegistryMongoDB.js @@ -22,7 +22,7 @@ */ const logger = require('logops'); -const dbService = require('../../model/dbConn'); +const mongoose = require('mongoose'); const intoTrans = require('../common/domain').intoTrans; const errors = require('../../errors'); const Command = require('../../model/Command'); @@ -45,24 +45,26 @@ function findCommand(service, subservice, deviceId, name, callback) { query.select({ __v: 0 }); - query.exec(function handleGet(error, data) { - if (error) { + query + .exec({}) + .then((data) => { + if (data) { + callback(null, data); + } else { + logger.debug( + context, + 'Command for DeviceID [%j] with name [%j] not found with [%j]', + deviceId, + name, + queryObj + ); + callback(new errors.CommandNotFound(name, queryObj)); + } + }) + .catch((error) => { logger.debug(context, 'Internal MongoDB Error getting command: %s', error); - callback(new errors.InternalDbError(error)); - } else if (data) { - callback(null, data); - } else { - logger.debug( - context, - 'Command for DeviceID [%j] with name [%j] not found with [%j]', - deviceId, - name, - queryObj - ); - callback(new errors.CommandNotFound(name, queryObj)); - } - }); + }); } function updateCommand(service, subservice, deviceId, command, callback) { @@ -72,9 +74,14 @@ function updateCommand(service, subservice, deviceId, command, callback) { } else { commandDAO.value = command.value; - commandDAO.save(function (error) { - callback(error, commandDAO.toObject()); - }); + commandDAO + .save({}) + .then((commandDAOs) => { + callback(null, commandDAOs.toObject()); + }) + .catch((error) => { + callback(error); + }); } }); } @@ -94,15 +101,15 @@ function createCommand(service, subservice, deviceId, command, callback) { logger.debug(context, 'Storing command for deviceId [%s] with name [%s]', deviceId, command.name); - commandObj.save(function saveHandler(error, commandDAO) { - if (error) { + commandObj + .save({}) + .then((commandDAO) => { + callback(null, commandDAO.toObject()); + }) + .catch((error) => { logger.debug(context, 'Error storing command information: %s', error); - callback(new errors.InternalDbError(error)); - } else { - callback(null, commandDAO.toObject()); - } - }); + }); } function addCommand(service, subservice, deviceId, command, callback) { @@ -129,16 +136,33 @@ function listCommands(service, subservice, deviceId, callback) { condition.deviceId = deviceId; const query = Command.model.find(condition).sort(); - - async.series( - [query.exec.bind(query), Command.model.countDocuments.bind(Command.model, condition)], - function (error, results) { - callback(error, { - count: results[1], - commands: results[0].map(toObjectFn) + const queryCount = Command.model.countDocuments(condition); + function funcQuery(cb) { + query + .exec({}) + .then((res) => { + cb(null, res); + }) + .catch((error) => { + cb(error); }); - } - ); + } + function funcQueryCount(cb) { + queryCount + .exec({}) + .then((res) => { + cb(null, res); + }) + .catch((error) => { + cb(error); + }); + } + async.series([funcQuery, funcQueryCount], function (error, results) { + callback(error, { + count: results[1], + commands: results[0].map(toObjectFn) + }); + }); } function remove(service, subservice, deviceId, name, callback) { @@ -155,26 +179,29 @@ function remove(service, subservice, deviceId, name, callback) { if (error) { callback(error); } else { - Command.model.deleteOne({ _id: command._id }, function (error, commandResult) { - if (error) { + const query = Command.model.deleteOne({ _id: command._id }); + query + .exec({}) + .then((commandResult) => { + if (commandResult && commandResult.result && commandResult.result.n === 1) { + logger.debug(context, 'Command [%s] successfully removed.', name); + + callback(null, commandResult); + } else { + const deviceInfo = { + service, + subservice, + deviceId, + name + }; + logger.debug(context, 'Command [%s] not found for removal with %j', name, deviceInfo); + callback(new errors.CommandNotFound(name, deviceInfo)); + } + }) + .catch((error) => { logger.debug(context, 'Internal MongoDB Error getting command: %s', error); - callback(new errors.InternalDbError(error)); - } else if (commandResult && commandResult.result && commandResult.result.n === 1) { - logger.debug(context, 'Command [%s] successfully removed.', name); - - callback(null, commandResult); - } else { - const deviceInfo = { - service, - subservice, - deviceId, - name - }; - logger.debug(context, 'Command [%s] not found for removal with %j', name, deviceInfo); - callback(new errors.CommandNotFound(name, deviceInfo)); - } - }); + }); } }); } @@ -190,35 +217,48 @@ function listToObject(commandList) { } function removeFromDate(creationDate, callback) { - const query = { creationDate: { $lt: creationDate } }; + const condition = { creationDate: { $lt: creationDate } }; if (Command.model) { - Command.model.find(query).exec(function (error, commandList) { - if (!error && commandList && commandList.length > 0) { - Command.model.deleteOne(query, function (error) { - if (error) { - logger.debug(context, 'Internal MongoDB Error removing expired commands: %s', error); - - callback(new errors.InternalDbError(error)); - } else { - logger.debug( - context, - 'Expired commands successfully removed from MongoDB for date [%s]', - creationDate - ); - - callback(null, listToObject(commandList)); - } - }); - } - }); + const query = Command.model.find(condition); + query + .exec({}) + .then((commandList) => { + if (commandList && commandList.length > 0) { + const queryDel = Command.model.deleteOne(condition); + queryDel + .exec({}) + .then(() => { + logger.debug( + context, + 'Expired commands successfully removed from MongoDB for date [%s]', + creationDate + ); + callback(null, listToObject(commandList)); + }) + .catch((error) => { + logger.debug(context, 'Internal MongoDB Error removing expired commands: %s', error); + callback(new errors.InternalDbError(error)); + }); + } + }) + .catch((error) => { + logger.debug(context, 'Internal MongoDB Error looking for expired commands: %s', error); + }); } else { callback(null, []); } } function clear(callback) { - dbService.db.db.dropDatabase(callback); + mongoose.connection + .dropDatabase() + .then(() => { + callback(null); + }) + .catch((error) => { + callback(error); + }); } function init(newConfig, callback) { diff --git a/lib/services/devices/deviceRegistryMongoDB.js b/lib/services/devices/deviceRegistryMongoDB.js index d966a4c62..4086f1933 100644 --- a/lib/services/devices/deviceRegistryMongoDB.js +++ b/lib/services/devices/deviceRegistryMongoDB.js @@ -22,7 +22,7 @@ */ const logger = require('logops'); -const dbService = require('../../model/dbConn'); +const mongoose = require('mongoose'); const config = require('../../commonConfig'); const fillService = require('./../common/domain').fillService; const alarmsInt = require('../common/alarmManagement').intercept; @@ -60,24 +60,6 @@ const attributeList = [ 'payloadType' ]; -/** - * Generates a handler for the save device operations. The handler will take the customary error and the saved device - * as the parameters (and pass the serialized DAO as the callback value). - * - * @return {Function} The generated handler. - */ -function saveDeviceHandler(callback) { - return function saveHandler(error, deviceDAO) { - if (error) { - logger.debug(fillService(context, deviceDAO), 'Error storing device information: %s', error); - - callback(new errors.InternalDbError(error)); - } else { - callback(null, deviceDAO.toObject()); - } - }; -} - /** * Create a new register for a device. The device object should contain the id, type and registrationId * @@ -97,8 +79,12 @@ function storeDevice(newDevice, callback) { logger.debug(context, 'Storing device with id [%s] and type [%s]', newDevice.id, newDevice.type); - deviceObj.save(function saveHandler(error, deviceDAO) { - if (error) { + deviceObj + .save({}) + .then((deviceDAO) => { + callback(null, deviceDAO.toObject()); + }) + .catch((error) => { if (error.code === 11000) { logger.debug(context, 'Tried to insert a device with duplicate ID in the database: %s', error); @@ -108,10 +94,7 @@ function storeDevice(newDevice, callback) { callback(new errors.InternalDbError(error)); } - } else { - callback(null, deviceDAO.toObject()); - } - }); + }); } /** @@ -132,17 +115,17 @@ function removeDevice(id, apikey, service, subservice, callback) { } logger.debug(context, 'Removing device with id [%s]', id); - Device.model.deleteOne(condition, function (error) { - if (error) { - logger.debug(context, 'Internal MongoDB Error getting device: %s', error); - - callback(new errors.InternalDbError(error)); - } else { + const query = Device.model.deleteOne(condition); + query + .exec({}) + .then(() => { logger.debug(context, 'Device [%s] successfully removed.', id); - callback(null); - } - }); + }) + .catch((error) => { + logger.debug(context, 'Internal MongoDB Error getting device: %s', error); + callback(new errors.InternalDbError(error)); + }); } /** @@ -170,7 +153,7 @@ function listDevices(type, service, subservice, limit, offset, callback) { } const query = Device.model.find(condition).sort(); - + const queryCount = Device.model.countDocuments(condition); if (limit) { query.limit(parseInt(limit, 10)); } @@ -179,36 +162,55 @@ function listDevices(type, service, subservice, limit, offset, callback) { query.skip(parseInt(offset, 10)); } - async.series( - [query.exec.bind(query), Device.model.countDocuments.bind(Device.model, condition)], - function (error, results) { - callback(error, { - count: results[1], - devices: results[0] + function funcQuery(cb) { + query + .exec({}) + .then((res) => { + cb(null, res); + }) + .catch((error) => { + cb(error); }); - } - ); + } + function funcQueryCount(cb) { + queryCount + .exec({}) + .then((res) => { + cb(null, res); + }) + .catch((error) => { + cb(error); + }); + } + async.series([funcQuery, funcQueryCount], function (error, results) { + callback(error, { + count: results[1], + devices: results[0] + }); + }); } function findOneInMongoDB(queryParams, id, callback) { const query = Device.model.findOne(queryParams); query.select({ __v: 0 }); - - query.lean().exec(function handleGet(error, data) { - if (error) { + query.lean(); + + query + .exec({}) + .then((data) => { + if (data) { + context = fillService(context, data); + logger.debug(context, 'Device data found: %j', data); + callback(null, data); + } else { + logger.debug(context, 'Device [%s] not found.', id); + callback(new errors.DeviceNotFound(id, queryParams)); + } + }) + .catch((error) => { logger.debug(context, 'Internal MongoDB Error getting device: %s', error); - callback(new errors.InternalDbError(error)); - } else if (data) { - context = fillService(context, data); - logger.debug(context, 'Device data found: %j', data); - callback(null, data); - } else { - logger.debug(context, 'Device [%s] not found.', id); - - callback(new errors.DeviceNotFound(id, queryParams)); - } - }); + }); } /** @@ -264,20 +266,23 @@ function getByNameAndType(name, type, service, servicepath, callback) { const query = Device.model.findOne(optionsQuery); query.select({ __v: 0 }); - - query.lean().exec(function handleGet(error, data) { - if (error) { + query.lean(); + query + .exec({}) + .then((data) => { + if (data) { + context = fillService(context, data); + logger.debug(context, 'Device data found: %j', data); + callback(null, data); + } else { + logger.debug(context, 'Device [%s] not found.', name); + callback(new errors.DeviceNotFound(name, optionsQuery)); + } + }) + .catch((error) => { logger.debug(context, 'Internal MongoDB Error getting device: %s', error); - callback(new errors.InternalDbError(error)); - } else if (data) { - callback(null, data); - } else { - logger.debug(context, 'Device [%s] not found.', name); - - callback(new errors.DeviceNotFound(name, optionsQuery)); - } - }); + }); } function getByName(name, service, servicepath, callback) { @@ -318,7 +323,15 @@ function update(previousDevice, device, callback) { /* eslint-disable-next-line new-cap */ const deviceObj = new Device.model(data); deviceObj.isNew = false; - deviceObj.save(saveDeviceHandler(callback)); + deviceObj + .save({}) + .then((deviceDAO) => { + callback(null, deviceDAO.toObject()); + }) + .catch((error) => { + logger.debug(fillService(context, device), 'Error storing device information: %s', error); + callback(new errors.InternalDbError(error)); + }); } }); } @@ -327,7 +340,14 @@ function update(previousDevice, device, callback) { * Cleans all the information in the database, leaving it in a clean state. */ function clear(callback) { - dbService.db.db.dropDatabase(callback); + mongoose.connection + .dropDatabase() + .then(() => { + callback(null); + }) + .catch((error) => { + callback(error); + }); } function itemToObject(i) { @@ -355,20 +375,21 @@ function getDevicesByAttribute(name, value, service, subservice, callback) { const query = Device.model.find(filter); query.select({ __v: 0 }); - - query.exec(function handleGet(error, devices) { - if (error) { + query.lean(); + query + .exec({}) + .then((devices) => { + if (devices) { + callback(null, devices.map(itemToObject)); + } else { + logger.debug(context, 'Device [%s] not found.', name); + callback(new errors.DeviceNotFound(name, filter)); + } + }) + .catch((error) => { logger.debug(context, 'Internal MongoDB Error getting device: %s', error); - callback(new errors.InternalDbError(error)); - } else if (devices) { - callback(null, devices.map(itemToObject)); - } else { - logger.debug(context, 'Device [%s] not found.', name); - - callback(new errors.DeviceNotFound(name, filter)); - } - }); + }); } exports.getDevicesByAttribute = alarmsInt(constants.MONGO_ALARM, getDevicesByAttribute); diff --git a/lib/services/groups/groupRegistryMongoDB.js b/lib/services/groups/groupRegistryMongoDB.js index 740a3fad7..4cd8c4fdb 100644 --- a/lib/services/groups/groupRegistryMongoDB.js +++ b/lib/services/groups/groupRegistryMongoDB.js @@ -24,7 +24,7 @@ */ const logger = require('logops'); -const dbService = require('../../model/dbConn'); +const mongoose = require('mongoose'); const intoTrans = require('../common/domain').intoTrans; const fillService = require('./../common/domain').fillService; const alarmsInt = require('../common/alarmManagement').intercept; @@ -64,25 +64,6 @@ const attributeList = [ 'payloadType' ]; -/** - * Generates a handler for the save device group operations. The handler will take the customary error and the saved - * device group as the parameters (and pass the serialized DAO as the callback value). - * - * @return {Function} The generated handler. - */ -function saveGroupHandler(groupDAO, callback) { - /* eslint-disable-next-line no-unused-vars */ - return function saveHandler(error, result) { - if (error) { - logger.debug(fillService(context, groupDAO), 'Error storing device group information: %s', error); - - callback(new errors.InternalDbError(error)); - } else { - callback(null, groupDAO.toObject()); - } - }; -} - function createGroup(group, callback) { /* eslint-disable-next-line new-cap */ const groupObj = new Group.model(); @@ -99,9 +80,12 @@ function createGroup(group, callback) { groupObj.apikey, groupObj.resource ); - - groupObj.save(function saveHandler(error, groupDAO) { - if (error) { + groupObj + .save({}) + .then((groupDAO) => { + callback(null, groupDAO.toObject()); + }) + .catch((error) => { if (error.code === 11000) { logger.debug( context, @@ -109,17 +93,12 @@ function createGroup(group, callback) { group.resource, group.apikey ); - callback(new errors.DuplicateGroup(group)); } else { logger.debug(context, 'Error storing device group information: %s', error); - callback(new errors.InternalDbError(error)); } - } else { - callback(null, groupDAO.toObject()); - } - }); + }); } /** @@ -141,7 +120,7 @@ function listGroups(service, limit, offset, callback) { } const query = Group.model.find(condition).sort(); - + const queryCount = Group.model.countDocuments(condition); if (limit) { query.limit(parseInt(limit, 10)); } @@ -149,16 +128,32 @@ function listGroups(service, limit, offset, callback) { if (offset) { query.skip(parseInt(offset, 10)); } - - async.series( - [query.exec.bind(query), Group.model.countDocuments.bind(Group.model, condition)], - function (error, results) { - callback(error, { - count: results[1], - services: results[0].map(toObjectFn) + function funcQuery(cb) { + query + .exec({}) + .then((res) => { + cb(null, res); + }) + .catch((error) => { + cb(error); }); - } - ); + } + function funcQueryCount(cb) { + queryCount + .exec({}) + .then((res) => { + cb(null, res); + }) + .catch((error) => { + cb(error); + }); + } + async.series([funcQuery, funcQueryCount], function (error, results) { + callback(error, { + count: results[1], + services: results[0].map(toObjectFn) + }); + }); } function getById(id, callback) { @@ -168,21 +163,22 @@ function getById(id, callback) { const query = Group.model.findOne({ _id: id }); query.select({ __v: 0 }); - query.lean().exec(function handleGet(error, data) { - if (error) { + query + .exec({}) + .then((data) => { + if (data) { + context = fillService(context, data); + logger.debug(context, 'Device group data found: %j', data); + callback(null, data); + } else { + logger.debug(context, 'Device group [%s] not found.', id); + callback(new errors.DeviceGroupNotFound(id)); + } + }) + .catch((error) => { logger.debug(context, 'Internal MongoDB Error getting group: %s', error); - callback(new errors.InternalDbError(error)); - } else if (data) { - context = fillService(context, data); - logger.debug(context, 'Device group data found: %j', data); - callback(null, data); - } else { - logger.debug(context, 'Device group [%s] not found.', id); - - callback(new errors.DeviceGroupNotFound(id)); - } - }); + }); } /** @@ -203,34 +199,56 @@ function find(service, subservice, callback) { condition.subservice = subservice; const query = Group.model.find(condition).sort(); - - async.series( - [query.exec.bind(query), Group.model.countDocuments.bind(Group.model, condition)], - function (error, results) { - callback(error, { - count: results[1], - services: results[0].map(toObjectFn) + const queryCount = Group.model.countDocuments(condition); + function funcQuery(cb) { + query + .exec({}) + .then((res) => { + cb(null, res); + }) + .catch((error) => { + cb(error); }); - } - ); + } + function funcQueryCount(cb) { + queryCount + .exec({}) + .then((res) => { + cb(null, res); + }) + .catch((error) => { + cb(error); + }); + } + async.series([funcQuery, funcQueryCount], function (error, results) { + callback(error, { + count: results[1], + services: results[0].map(toObjectFn) + }); + }); } function findOneInMongoDB(queryObj, fields, callback) { const query = Group.model.findOne(queryObj); query.select({ __v: 0 }); - query.lean().exec(function handleGet(error, data) { - if (error) { + query.lean(); + + query + .exec({}) + .then((data) => { + if (data) { + context = fillService(context, data); + logger.debug(context, 'Device group data found: %j', data); + callback(null, data); + } else { + logger.debug(context, 'Device group for fields [%j] not found: [%j]', fields, queryObj); + callback(new errors.DeviceGroupNotFound(fields, queryObj)); + } + }) + .catch((error) => { logger.debug(context, 'Internal MongoDB Error getting group: %s', error); callback(new errors.InternalDbError(error)); - } else if (data) { - context = fillService(context, data); - logger.debug(context, 'Device group data found: %j', data); - callback(null, data); - } else { - logger.debug(context, 'Device group for fields [%j] not found: [%j]', fields, queryObj); - callback(new errors.DeviceGroupNotFound(fields, queryObj)); - } - }); + }); } function findBy(fields) { @@ -269,7 +287,15 @@ function update(id, body, callback) { /* eslint-disable-next-line new-cap */ const groupObj = new Group.model(group); groupObj.isNew = false; - groupObj.save(saveGroupHandler(groupObj, callback)); + groupObj + .save({}) + .then((groupDAO) => { + callback(null, groupDAO.toObject()); + }) + .catch((error) => { + logger.debug(fillService(context, group), 'Error storing device group information: %s', error); + callback(new errors.InternalDbError(error)); + }); } }); } @@ -281,16 +307,18 @@ function remove(id, callback) { if (error) { callback(error); } else { - Group.model.deleteOne({ _id: id }, function (error) { - if (error) { - logger.debug(context, 'Internal MongoDB Error getting device: %s', error); + const query = Group.model.deleteOne({ _id: id }); + query + .exec({}) + .then(() => { + logger.debug(context, 'Device group [%s] successfully removed.', id); + callback(null, deviceGroup); + }) + .catch((error) => { + logger.debug(context, 'Internal MongoDB Error getting device group: %s', error); callback(new errors.InternalDbError(error)); - } else { - logger.debug(context, 'Device [%s] successfully removed.', id); - callback(null, deviceGroup); - } - }); + }); } }); } @@ -300,7 +328,14 @@ function init(newConfig, callback) { } function clear(callback) { - dbService.db.db.dropDatabase(callback); + mongoose.connection + .dropDatabase() + .then(() => { + callback(null); + }) + .catch((error) => { + callback(error); + }); } exports.create = alarmsInt(constants.MONGO_ALARM + '_01', intoTrans(context, createGroup)); diff --git a/package.json b/package.json index 45cf26af5..d24098697 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "logops": "2.1.2", "moment": "~2.29.2", "moment-timezone": "~0.5.34", - "mongoose": "5.13.22", + "mongoose": "8.8.4", "query-string": "7.1.1", "revalidator": "~0.3.1", "underscore": "~1.13.4", @@ -69,7 +69,7 @@ "husky": "~4.2.5", "lint-staged": "~12.3.8", "mocha": "10.0.0", - "mongodb": "4.17.1", + "mongodb": "4.17.2", "nock": "13.2.7", "nyc": "~15.1.0", "prettier": "~2.7.1", diff --git a/test/unit/mongodb/mongodb-connectionoptions-test.js b/test/unit/mongodb/mongodb-connectionoptions-test.js index 439a8c9a9..51cb163ca 100644 --- a/test/unit/mongodb/mongodb-connectionoptions-test.js +++ b/test/unit/mongodb/mongodb-connectionoptions-test.js @@ -75,8 +75,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -88,8 +86,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:98765/' + dbConn.DEFAULT_DB_NAME, options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -101,8 +97,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/examples', options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -114,9 +108,7 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - replicaSet: 'rs0', - useNewUrlParser: true, - useUnifiedTopology: true + replicaSet: 'rs0' } } }, @@ -128,8 +120,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -141,8 +131,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -158,9 +146,7 @@ describe('dbConn.configureDb', function () { auth: { user: 'user01', password: 'pass01' - }, - useNewUrlParser: true, - useUnifiedTopology: true + } } } }, @@ -172,8 +158,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -194,9 +178,7 @@ describe('dbConn.configureDb', function () { auth: { user: 'user01', password: 'pass01' - }, - useNewUrlParser: true, - useUnifiedTopology: true + } } } }, @@ -208,9 +190,7 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - ssl: true, - useNewUrlParser: true, - useUnifiedTopology: true + ssl: true } } }, @@ -224,8 +204,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME + '?retryWrites=true', options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -243,8 +221,6 @@ describe('dbConn.configureDb', function () { dbConn.DEFAULT_DB_NAME + '?retryWrites=true&readPreference=nearest', options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -256,8 +232,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -269,8 +243,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -282,8 +254,6 @@ describe('dbConn.configureDb', function () { expected: { url: 'mongodb://example.com:27017/' + dbConn.DEFAULT_DB_NAME, options: { - useNewUrlParser: true, - useUnifiedTopology: true } } }, @@ -312,9 +282,7 @@ describe('dbConn.configureDb', function () { user: 'user01', password: 'pass01' }, - ssl: true, - useNewUrlParser: true, - useUnifiedTopology: true + ssl: true } } } @@ -330,7 +298,7 @@ describe('dbConn.configureDb', function () { const cfg = Object.assign({}, iotAgentConfig, { mongodb: params.mongodb }); - stub = sinon.stub(mongoose, 'createConnection').callsFake(function (url, options, fn) { + stub = sinon.stub(mongoose, 'connect').callsFake(function (url, options, fn) { url.should.be.equal(params.expected.url); options.should.be.eql(params.expected.options); done(); @@ -365,7 +333,7 @@ describe('dbConn.configureDb', function () { const cfg = Object.assign({}, iotAgentConfig, { mongodb: params.mongodb }); - stub = sinon.stub(mongoose, 'createConnection').callsFake(function (url, options, fn) { + stub = sinon.stub(mongoose, 'connect').callsFake(function (url, options, fn) { should.fail(); }); config.setConfig(cfg); diff --git a/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js b/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js index 6940ed5d0..c3c2f2d4a 100644 --- a/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +++ b/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js @@ -342,15 +342,6 @@ describe('NGSI-v2 - Polling commands', function () { }; beforeEach(function (done) { - statusAttributeMock = nock('http://192.168.1.1:1026') - .matchHeader('fiware-service', 'smartgondor') - .matchHeader('fiware-servicepath', 'gardens') - .post( - '/v2/entities?options=upsert', - utils.readExampleFile('./test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus.json') - ) - .reply(204); - statusAttributeMock = nock('http://192.168.1.1:1026') .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', 'gardens')