diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index f026dbbd9..542bccb73 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,3 +1 @@ -- Add: extend handleNotificationNgsi2 to allow receive commands from CB notifications (#1455) -- Add: `POST /iot/op/delete` operation to delete multiple devices at once (#1578) -- Fix: store device subscriptions updates (#1086) \ No newline at end of file +- Add: extend handleNotificationNgsi2 to allow receive commands from CB notifications (#1455) \ No newline at end of file diff --git a/doc/api.md b/doc/api.md index 9947eaa6f..8849cb276 100644 --- a/doc/api.md +++ b/doc/api.md @@ -1765,7 +1765,8 @@ Example: _**Response code**_ - `204` `OK` if successful. -- `404` `NOT FOUND` if one or several devices were not found in the database. Note that although a 404 error is returned the existing devices are deleted. +- `404` `NOT FOUND` if one or several devices were not found in the database. Note that although a 404 error is + returned the existing devices are deleted. - `500` `SERVER ERROR` if there was any error not considered above. ## Miscellaneous API diff --git a/lib/errors.js b/lib/errors.js index 6eb23e5e7..08a57e062 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -36,9 +36,15 @@ class UnregistrationError { } } class EntityGenericError { - constructor(id, type, details, code) { + constructor(id, type, typeInformation, details, code) { this.name = 'ENTITY_GENERIC_ERROR'; - this.message = 'Error accesing entity data for device: ' + id + ' of type: ' + type; + this.message = + 'Error accesing entity data for device: ' + + id + + ' of type: ' + + type + + ' and ' + + JSON.stringify(typeInformation); this.details = details || {}; this.code = code || 200; } @@ -77,36 +83,44 @@ class UnsupportedContentType { } } class TypeNotFound { - constructor(id, type) { + constructor(id, type, typeInformation) { this.name = 'TYPE_NOT_FOUND'; - this.message = 'Type : ' + type + ' not found for device with id: ' + id; + this.message = + 'Type : ' + type + ' not found for device with id: ' + id + ' with: ' + JSON.stringify(typeInformation); this.code = 500; } } class MissingAttributes { - constructor(msg) { + constructor(msg, device) { this.name = 'MISSING_ATTRIBUTES'; - this.message = 'The request was not well formed:' + msg; + this.message = 'The request was not well formed:' + msg + ' with: ' + JSON.stringify(device); } } class DeviceNotFound { - constructor(id) { + constructor(id, typeInformation) { this.name = 'DEVICE_NOT_FOUND'; - this.message = 'No device was found with id:' + id; + this.message = 'No device was found with id:' + id + ' and ' + JSON.stringify(typeInformation); this.code = 404; } } class DuplicateDeviceId { - constructor(id) { + constructor(device) { this.name = 'DUPLICATE_DEVICE_ID'; - this.message = 'A device with the same pair (Service, DeviceId) was found:' + id; + this.message = + 'A device with the same pair (Service, DeviceId) was found:' + device.id + ' and ' + JSON.stringify(device); this.code = 409; } } class DuplicateGroup { - constructor(res, key) { + constructor(group) { this.name = 'DUPLICATE_GROUP'; - this.message = 'A device configuration already exists for resource ' + res + ' and API Key ' + key; + this.message = + 'A device configuration already exists for resource ' + + group.resource + + ' and API Key ' + + group.apikey + + ' and ' + + JSON.stringify(group); this.code = 409; } } @@ -170,9 +184,13 @@ class WrongSyntax { } } class CommandNotFound { - constructor(name) { + constructor(name, typeInformation) { this.name = 'COMMAND_NOT_FOUND'; - this.message = "Couldn't update the command because no command with the name [" + name + '] was found.'; + this.message = + "Couldn't update the command because no command with the name [" + + name + + '] was found with ' + + JSON.stringify(typeInformation); this.code = 400; } } @@ -201,9 +219,10 @@ class DeviceGroupNotFound { } } class GroupNotFound { - constructor(service, subservice) { + constructor(service, subservice, type) { this.name = 'GROUP_NOT_FOUND'; - this.message = 'Group not found for service [' + service + '] and subservice [' + subservice + ']'; + this.message = + 'Group not found for service [' + service + '] subservice [' + subservice + '] and type [' + type + ']'; this.code = 404; } } @@ -229,16 +248,22 @@ class BadAnswer { } } class BadTimestamp { - constructor(payload, entityName) { + constructor(payload, entityName, typeInformation) { this.name = 'BAD_TIMESTAMP'; - this.message = 'Invalid ISO8601 timestamp [' + payload + '] in [' + entityName + ']'; + this.message = + 'Invalid ISO8601 timestamp [' + + payload + + '] in [' + + entityName + + '] with: ' + + JSON.stringify(typeInformation); this.code = 400; } } class BadGeocoordinates { - constructor(payload) { + constructor(payload, device) { this.name = 'BAD_GEOCOORDINATES'; - this.message = 'Invalid rfc7946 coordinates [' + payload + ']'; + this.message = 'Invalid rfc7946 coordinates [' + payload + '] in ' + JSON.stringify(device); this.code = 400; } } diff --git a/lib/services/commands/commandRegistryMemory.js b/lib/services/commands/commandRegistryMemory.js index b1948d044..e0a59577c 100644 --- a/lib/services/commands/commandRegistryMemory.js +++ b/lib/services/commands/commandRegistryMemory.js @@ -75,7 +75,12 @@ function updateCommand(service, subservice, deviceId, command, callback) { callback(null, foundCommand); } else { - callback(new errors.CommandNotFound(command.name)); + const deviceInfo = { + service, + subservice, + deviceId + }; + callback(new errors.CommandNotFound(command.name, deviceInfo)); } } @@ -144,7 +149,12 @@ function remove(service, subservice, deviceId, name, callback) { delete registeredCommands[foundCommand._id]; callback(null, foundCommand); } else { - callback(new errors.CommandNotFound(name)); + const deviceInfo = { + service, + subservice, + deviceId + }; + callback(new errors.CommandNotFound(name, deviceInfo)); } } diff --git a/lib/services/commands/commandRegistryMongoDB.js b/lib/services/commands/commandRegistryMongoDB.js index c94d93efc..6a7192687 100644 --- a/lib/services/commands/commandRegistryMongoDB.js +++ b/lib/services/commands/commandRegistryMongoDB.js @@ -39,7 +39,7 @@ function findCommand(service, subservice, deviceId, name, callback) { name }; - logger.debug(context, 'Looking for command [%s] for device [%s]', name, deviceId); + logger.debug(context, 'Looking for command [%s] for device [%s] with [%j]', name, deviceId, queryObj); const query = Command.model.findOne(queryObj); @@ -53,8 +53,14 @@ function findCommand(service, subservice, deviceId, name, callback) { } else if (data) { callback(null, data); } else { - logger.debug(context, 'Command for DeviceID [%j] with name [%j] not found', deviceId, name); - callback(new errors.CommandNotFound(name)); + logger.debug( + context, + 'Command for DeviceID [%j] with name [%j] not found with [%j]', + deviceId, + name, + queryObj + ); + callback(new errors.CommandNotFound(name, queryObj)); } }); } @@ -173,9 +179,14 @@ function remove(service, subservice, deviceId, name, callback) { callback(null, commandResult); } else { - logger.debug(context, 'Command [%s] not found for removal.', name); - - callback(new errors.CommandNotFound(name)); + 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)); } }); } diff --git a/lib/services/devices/deviceRegistryMemory.js b/lib/services/devices/deviceRegistryMemory.js index 7f7408dd0..b801f2744 100644 --- a/lib/services/devices/deviceRegistryMemory.js +++ b/lib/services/devices/deviceRegistryMemory.js @@ -54,7 +54,7 @@ function storeDevice(newDevice, callback) { } if (registeredDevices[newDevice.service][newDevice.id]) { - callback(new errors.DuplicateDeviceId(newDevice.id)); + callback(new errors.DuplicateDeviceId(newDevice)); } else { registeredDevices[newDevice.service][newDevice.id] = deepClone(newDevice); registeredDevices[newDevice.service][newDevice.id].creationDate = Date.now(); diff --git a/lib/services/devices/deviceRegistryMongoDB.js b/lib/services/devices/deviceRegistryMongoDB.js index f9b161581..291848ce2 100644 --- a/lib/services/devices/deviceRegistryMongoDB.js +++ b/lib/services/devices/deviceRegistryMongoDB.js @@ -102,7 +102,7 @@ function storeDevice(newDevice, callback) { if (error.code === 11000) { logger.debug(context, 'Tried to insert a device with duplicate ID in the database: %s', error); - callback(new errors.DuplicateDeviceId(newDevice.id)); + callback(new errors.DuplicateDeviceId(newDevice)); } else { logger.debug(context, 'Error storing device information: %s', error); @@ -206,7 +206,7 @@ function findOneInMongoDB(queryParams, id, callback) { } else { logger.debug(context, 'Device [%s] not found.', id); - callback(new errors.DeviceNotFound(id)); + callback(new errors.DeviceNotFound(id, queryParams)); } }); } @@ -282,7 +282,7 @@ function getByNameAndType(name, type, service, servicepath, callback) { } else { logger.debug(context, 'Device [%s] not found.', name); - callback(new errors.DeviceNotFound(name)); + callback(new errors.DeviceNotFound(name, optionsQuery)); } }); } @@ -373,7 +373,7 @@ function getDevicesByAttribute(name, value, service, subservice, callback) { } else { logger.debug(context, 'Device [%s] not found.', name); - callback(new errors.DeviceNotFound(name)); + callback(new errors.DeviceNotFound(name, filter)); } }); } diff --git a/lib/services/devices/deviceService.js b/lib/services/devices/deviceService.js index 45c2761b3..2d773ef78 100644 --- a/lib/services/devices/deviceService.js +++ b/lib/services/devices/deviceService.js @@ -244,7 +244,7 @@ function registerDevice(deviceObj, callback) { /* eslint-disable-next-line no-unused-vars */ function (error, device) { if (!error) { - innerCb(new errors.DuplicateDeviceId(deviceObj.id)); + innerCb(new errors.DuplicateDeviceId(deviceObj)); } else { innerCb(); } @@ -646,7 +646,7 @@ function findOrCreate(deviceId, apikey, group, callback) { newDevice, group ); - callback(new errors.DeviceNotFound(deviceId)); + callback(new errors.DeviceNotFound(deviceId, newDevice)); } } else { callback(error); @@ -671,7 +671,7 @@ function retrieveDevice(deviceId, apiKey, callback) { } else { logger.error(context, "Couldn't find device data for APIKey [%s] and DeviceId[%s]", deviceId, apiKey); - callback(new errors.DeviceNotFound(deviceId)); + callback(new errors.DeviceNotFound(deviceId, { apikey: apiKey })); } }); } else { diff --git a/lib/services/devices/devices-NGSI-LD.js b/lib/services/devices/devices-NGSI-LD.js index 66c6f14c9..42487e6fb 100644 --- a/lib/services/devices/devices-NGSI-LD.js +++ b/lib/services/devices/devices-NGSI-LD.js @@ -93,7 +93,7 @@ function updateEntityHandlerNgsiLD(deviceData, updatedDevice, callback) { body ); - const errorObj = new errors.EntityGenericError(deviceData.id, deviceData.type, body); + const errorObj = new errors.EntityGenericError(deviceData.id, deviceData.type, deviceData, body); callback(errorObj); } @@ -184,7 +184,7 @@ function updateEntityNgsiLD(deviceData, updatedDevice, callback) { */ function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) { if (!deviceObj.id || !deviceObj.type) { - callback(new errors.MissingAttributes('Id or device missing')); + callback(new errors.MissingAttributes('Id or device missing', deviceObj)); return; } @@ -216,7 +216,7 @@ function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) { callback(null, oldDevice); } else { - callback(new errors.DeviceNotFound(newDevice.id)); + callback(new errors.DeviceNotFound(newDevice.id, newDevice)); } } diff --git a/lib/services/devices/devices-NGSI-v2.js b/lib/services/devices/devices-NGSI-v2.js index acddbe083..77ee7431f 100644 --- a/lib/services/devices/devices-NGSI-v2.js +++ b/lib/services/devices/devices-NGSI-v2.js @@ -89,7 +89,13 @@ function updateEntityHandlerNgsi2(deviceData, updatedDevice, callback) { body ); - const errorObj = new errors.EntityGenericError(deviceData.id, deviceData.type, body, response.statusCode); + const errorObj = new errors.EntityGenericError( + deviceData.id, + deviceData.type, + deviceData, + body, + response.statusCode + ); callback(errorObj); } @@ -204,7 +210,7 @@ function updateEntityNgsi2(deviceData, updatedDevice, callback) { // Format any GeoJSON attrs properly options.json[att] = NGSIv2.formatGeoAttrs(options.json[att]); } catch (error) { - return callback(new errors.BadGeocoordinates(JSON.stringify(options.json))); + return callback(new errors.BadGeocoordinates(JSON.stringify(options.json), deviceData)); } } @@ -243,7 +249,7 @@ function updateEntityNgsi2(deviceData, updatedDevice, callback) { */ function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) { if (!deviceObj.id || !deviceObj.type) { - callback(new errors.MissingAttributes('Id or device missing')); + callback(new errors.MissingAttributes('Id or device missing', deviceObj)); return; } @@ -282,7 +288,7 @@ function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) { callback(null, oldDevice); } else { - callback(new errors.DeviceNotFound(newDevice.id)); + callback(new errors.DeviceNotFound(newDevice.id, newDevice)); } } diff --git a/lib/services/groups/groupRegistryMemory.js b/lib/services/groups/groupRegistryMemory.js index a4ef73a1d..5d0173175 100644 --- a/lib/services/groups/groupRegistryMemory.js +++ b/lib/services/groups/groupRegistryMemory.js @@ -50,7 +50,7 @@ function exists(group) { function createGroup(group, callback) { if (exists(group)) { - callback(new errors.DuplicateGroup(group.resource, group.apikey)); + callback(new errors.DuplicateGroup(group)); } else { const storeGroup = _.clone(group); diff --git a/lib/services/groups/groupRegistryMongoDB.js b/lib/services/groups/groupRegistryMongoDB.js index 8a5bdb9e9..740a3fad7 100644 --- a/lib/services/groups/groupRegistryMongoDB.js +++ b/lib/services/groups/groupRegistryMongoDB.js @@ -110,7 +110,7 @@ function createGroup(group, callback) { group.apikey ); - callback(new errors.DuplicateGroup(group.resource, group.apikey)); + callback(new errors.DuplicateGroup(group)); } else { logger.debug(context, 'Error storing device group information: %s', error); diff --git a/lib/services/groups/groupService.js b/lib/services/groups/groupService.js index d8e8a48bc..8f9d734f5 100644 --- a/lib/services/groups/groupService.js +++ b/lib/services/groups/groupService.js @@ -48,7 +48,7 @@ function validateGroup(group, callback) { return function (error, foundGroup) { logger.debug(context, 'generateDuplicateHander error %j and foundGroup %j', error, foundGroup); if (!error || (foundGroup && foundGroup.count > 0)) { - innerCb(new errors.DuplicateGroup(group.resource, group.apikey)); + innerCb(new errors.DuplicateGroup(group)); } else { innerCb(); } @@ -291,8 +291,14 @@ function getEffectiveApiKey(service, subservice, type, callback) { logger.debug(context, 'Using default API Key: %s', config.getConfig().defaultKey); callback(null, config.getConfig().defaultKey); } else { - logger.error(context, 'Could not find any API Key information for device.'); - callback(new errors.GroupNotFound(service, subservice)); + logger.error( + context, + 'Could not find any APIKey information for device in service %s subservice %s and type %s', + service, + subservice, + type + ); + callback(new errors.GroupNotFound(service, subservice, type)); } } diff --git a/lib/services/ngsi/entities-NGSI-LD.js b/lib/services/ngsi/entities-NGSI-LD.js index 79c20a535..be8512905 100644 --- a/lib/services/ngsi/entities-NGSI-LD.js +++ b/lib/services/ngsi/entities-NGSI-LD.js @@ -381,9 +381,9 @@ function generateNGSILDOperationHandler(operationName, entityName, typeInformati } if (errorField !== undefined) { - callback(new errors.DeviceNotFound(entityName)); + callback(new errors.DeviceNotFound(entityName, typeInformation)); } else { - callback(new errors.EntityGenericError(entityName, typeInformation.type, body)); + callback(new errors.EntityGenericError(entityName, typeInformation.type, typeInformation, body)); } } else { logger.debug(context, 'Unknown error executing ' + operationName + ' operation'); @@ -391,7 +391,15 @@ function generateNGSILDOperationHandler(operationName, entityName, typeInformati body = JSON.parse(body); } - callback(new errors.EntityGenericError(entityName, typeInformation.type, body, response.statusCode)); + callback( + new errors.EntityGenericError( + entityName, + typeInformation.type, + typeInformation, + body, + response.statusCode + ) + ); } }; } @@ -416,7 +424,7 @@ function sendQueryValueNgsiLD(entityName, attributes, typeInformation, token, ca options.method = 'GET'; if (!typeInformation || !typeInformation.type) { - callback(new errors.TypeNotFound(null, entityName)); + callback(new errors.TypeNotFound(null, entityName, typeInformation)); return; } @@ -529,7 +537,7 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c } if (!typeInformation || !typeInformation.type) { - callback(new errors.TypeNotFound(null, entityName)); + callback(new errors.TypeNotFound(null, entityName, typeInformation)); return; } const idTypeSSSList = pluginUtils.getIdTypeServSubServiceFromDevice(typeInformation); @@ -1015,7 +1023,7 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c } else if (!utils.IsValidTimestampedNgsi2(payload[n])) { // legacy check needed? logger.error(context, 'Invalid timestamp:%s', JSON.stringify(payload[0])); - callback(new errors.BadTimestamp(payload, entityName)); + callback(new errors.BadTimestamp(payload, entityName, typeInformation)); return; } } @@ -1044,7 +1052,7 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c options.json = [formatAsNGSILD(options.json)]; } } catch (error) { - return callback(new errors.BadGeocoordinates(JSON.stringify(payload))); + return callback(new errors.BadGeocoordinates(JSON.stringify(payload), typeInformation)); } if (typeInformation.active) { diff --git a/lib/services/ngsi/entities-NGSI-v2.js b/lib/services/ngsi/entities-NGSI-v2.js index 2238a4ed2..38d0a1471 100644 --- a/lib/services/ngsi/entities-NGSI-v2.js +++ b/lib/services/ngsi/entities-NGSI-v2.js @@ -31,7 +31,6 @@ const request = require('../../request-shim'); const alarms = require('../common/alarmManagement'); const errors = require('../../errors'); const pluginUtils = require('../../plugins/pluginUtils'); -const config = require('../../commonConfig'); const constants = require('../../constants'); const jexlParser = require('../../plugins/jexlParser'); const expressionPlugin = require('../../plugins/expressionPlugin'); @@ -173,9 +172,9 @@ function generateNGSI2OperationHandler(operationName, entityName, typeInformatio } if (errorField !== undefined) { - callback(new errors.DeviceNotFound(entityName)); + callback(new errors.DeviceNotFound(entityName, typeInformation)); } else { - callback(new errors.EntityGenericError(entityName, typeInformation.type, body)); + callback(new errors.EntityGenericError(entityName, typeInformation.type, typeInformation, body)); } } else { logger.debug(context, 'Unknown error executing ' + operationName + ' operation'); @@ -183,7 +182,15 @@ function generateNGSI2OperationHandler(operationName, entityName, typeInformatio body = JSON.parse(body); } - callback(new errors.EntityGenericError(entityName, typeInformation.type, body, response.statusCode)); + callback( + new errors.EntityGenericError( + entityName, + typeInformation.type, + typeInformation, + body, + response.statusCode + ) + ); } }; } @@ -225,7 +232,7 @@ function sendQueryValueNgsi2(entityName, attributes, typeInformation, token, cal options.method = 'GET'; if (!typeInformation || !typeInformation.type) { - callback(new errors.TypeNotFound(null, entityName)); + callback(new errors.TypeNotFound(null, entityName, typeInformation)); return; } @@ -279,7 +286,7 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call //Check mandatory information: type if (!typeInformation || !typeInformation.type) { - callback(new errors.TypeNotFound(null, entityName)); + callback(new errors.TypeNotFound(null, entityName, typeInformation)); return; } //Rename all measures with matches with id and type to measure_id and measure_type @@ -301,11 +308,9 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call jexlctxt = reduceAttrToPlainObject(idTypeSSSList, jexlctxt); //Managing timestamp (mustInsertTimeInstant flag to decide if we should insert Timestamp later on) - const mustInsertTimeInstant = - typeInformation.timestamp !== undefined - ? typeInformation.timestamp - : config.getConfig().timestamp !== undefined - ? config.getConfig().timestamp + const mustInsertTimeInstant = + typeInformation.timestamp !== undefined + ? typeInformation.timestamp : false; if (mustInsertTimeInstant) { @@ -317,7 +322,10 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call if (moment(plainMeasures[constants.TIMESTAMP_ATTRIBUTE], moment.ISO_8601, true).isValid()) { timestamp.value = plainMeasures[constants.TIMESTAMP_ATTRIBUTE]; } else { - callback(new errors.BadTimestamp(plainMeasures[constants.TIMESTAMP_ATTRIBUTE], entityName)); + callback( + new errors.BadTimestamp(plainMeasures[constants.TIMESTAMP_ATTRIBUTE], entityName, typeInformation) + ); + return; } } else if (!typeInformation.timezone) { timestamp.value = new Date().toISOString(); diff --git a/lib/services/ngsi/ngsiService.js b/lib/services/ngsi/ngsiService.js index aea6ef802..bbb253d11 100644 --- a/lib/services/ngsi/ngsiService.js +++ b/lib/services/ngsi/ngsiService.js @@ -144,9 +144,9 @@ function executeWithDeviceInformation(operationFunction) { // For preregistered devices, augment the existing deviceInformation with selected attributes. if (!callback) { callback = deviceInformation; - typeInformation = deviceGroup || configDeviceInfo; + typeInformation = deviceGroup || { ...config.getConfig(), ...configDeviceInfo }; } else { - typeInformation = deviceInformation; + typeInformation = { ...config.getConfig(), ...deviceInformation }; attributeList.forEach((key) => { typeInformation[key] = typeInformation[key] || (deviceGroup || {})[key] || (configDeviceInfo || {})[key]; @@ -236,7 +236,7 @@ function setCommandResult( if (commandInfo.length === 1) { exports.update(entityName, typeInformation.type, apikey, attributes, typeInformation, callback); } else { - callback(new errors.CommandNotFound(commandName)); + callback(new errors.CommandNotFound(commandName, typeInformation)); } }); } diff --git a/lib/services/ngsi/subscription-NGSI-LD.js b/lib/services/ngsi/subscription-NGSI-LD.js index 5ba7f71d1..4eb0f2129 100644 --- a/lib/services/ngsi/subscription-NGSI-LD.js +++ b/lib/services/ngsi/subscription-NGSI-LD.js @@ -62,6 +62,7 @@ function createSubscriptionHandlerNgsiLD(device, triggers, store, callback) { new errors.EntityGenericError( device.name, device.type, + device, { details: body }, @@ -183,6 +184,7 @@ function createUnsubscribeHandlerNgsiLD(device, id, callback) { new errors.EntityGenericError( device.name, device.type, + device, { details: body }, diff --git a/lib/services/ngsi/subscription-NGSI-v2.js b/lib/services/ngsi/subscription-NGSI-v2.js index aa31bede2..ccaf64f0c 100644 --- a/lib/services/ngsi/subscription-NGSI-v2.js +++ b/lib/services/ngsi/subscription-NGSI-v2.js @@ -64,6 +64,7 @@ function createSubscriptionHandlerNgsi2(device, triggers, store, callback) { new errors.EntityGenericError( device.name, device.type, + device, { details: body }, @@ -183,6 +184,7 @@ function createUnsubscribeHandlerNgsi2(device, id, callback) { new errors.EntityGenericError( device.name, device.type, + device, { details: body }, diff --git a/lib/services/northBound/contextServer-NGSI-LD.js b/lib/services/northBound/contextServer-NGSI-LD.js index a1016b9b8..e9c3d1b66 100644 --- a/lib/services/northBound/contextServer-NGSI-LD.js +++ b/lib/services/northBound/contextServer-NGSI-LD.js @@ -48,41 +48,36 @@ const overwritePaths = ['/ngsi-ld/v1/entities/:entity/attrs', '/ngsi-ld/v1/entit const updatePaths = ['/ngsi-ld/v1/entities/:entity/attrs', '/ngsi-ld/v1/entities/:entity/attrs/:attr']; const queryPaths = ['/ngsi-ld/v1/entities/:entity']; - /** * Replacement of NGSI-LD Null placeholders with real null values * */ -function replaceNGSILDNull(payload){ - Object.keys(payload).forEach((key) =>{ +function replaceNGSILDNull(payload) { + Object.keys(payload).forEach((key) => { const value = payload[key]; - if ( value === constants.NGSI_LD_NULL){ - payload[key] = null; - } else if (typeof value === 'object' && - !Array.isArray(value) && - value !== null){ + if (value === constants.NGSI_LD_NULL) { + payload[key] = null; + } else if (typeof value === 'object' && !Array.isArray(value) && value !== null) { payload[key] = replaceNGSILDNull(payload[key]); } - }) - return payload; + }); + return payload; } /** * Check to see if the payload or its subattributes contain null values * */ -function containsNulls(payload, result){ - Object.keys(payload).forEach((key) =>{ +function containsNulls(payload, result) { + Object.keys(payload).forEach((key) => { const value = payload[key]; - if ( value === null){ - result.nulls = true; - } else if (typeof value === 'object' && - !Array.isArray(value) && - value !== null){ + if (value === null) { + result.nulls = true; + } else if (typeof value === 'object' && !Array.isArray(value) && value !== null) { containsNulls(payload[key], result); } - }) - return result; + }); + return result; } /** @@ -90,23 +85,23 @@ function containsNulls(payload, result){ * to real nulls and checks for the presence of null and datasetId * */ -function preprocessNGSILD(req, res, next){ - res.locals.hasDatasetId = false; - const payload = req.body - if (payload && typeof payload === 'object'){ - Object.keys(payload).forEach((key) =>{ - if (_.isArray(payload[key])){ +function preprocessNGSILD(req, res, next) { + res.locals.hasDatasetId = false; + const payload = req.body; + if (payload && typeof payload === 'object') { + Object.keys(payload).forEach((key) => { + if (_.isArray(payload[key])) { payload[key].forEach((obj) => { - if (obj.datasetId){ + if (obj.datasetId) { res.locals.hasDatasetId = true; } }); - } else if (payload[key] && payload[key].datasetId && payload[key].datasetId !== '@none'){ - res.locals.hasDatasetId = true; - } + } else if (payload[key] && payload[key].datasetId && payload[key].datasetId !== '@none') { + res.locals.hasDatasetId = true; + } }); req.body = replaceNGSILDNull(payload); - const result = { nulls: false } + const result = { nulls: false }; containsNulls(payload, result); res.locals.hasNulls = result.nulls; } @@ -124,16 +119,23 @@ function preprocessNGSILD(req, res, next){ function validateNGSILD(supportNull, supportDatasetId) { return function validate(req, res, next) { if (!supportNull && res.locals.hasNulls) { - next(new errors.BadRequest('NGSI-LD Null found within the payload. This IoT Agent does not support nulls for this endpoint.')); + next( + new errors.BadRequest( + 'NGSI-LD Null found within the payload. This IoT Agent does not support nulls for this endpoint.' + ) + ); } else if (!supportDatasetId && res.locals.hasDatasetId) { - next(new errors.BadRequest('datasetId found within the payload. This IoT Agent does not support multi-attribute requests.')); + next( + new errors.BadRequest( + 'datasetId found within the payload. This IoT Agent does not support multi-attribute requests.' + ) + ); } else { next(); } }; } - /** * Extract metadata attributes from input. * @@ -413,34 +415,31 @@ function defaultQueryHandlerNgsiLD(id, type, service, subservice, attributes, ca * @param {Object} req Update request to generate Actions from */ function generateMergePatchActionNgsiLD(req, callback) { - const entityId = req.params.entity; - - function addAttributes(deviceData, body, attributes){ + function addAttributes(deviceData, body, attributes) { const keys = Object.keys(body); for (const j in deviceData) { if (keys.includes(deviceData[j].name)) { - const obj = body[deviceData[j].name] - if ( obj === null) { + const obj = body[deviceData[j].name]; + if (obj === null) { attributes.push({ type: deviceData[j].type, value: null, name: deviceData[j].name }); } else { - attributes.push({ + attributes.push({ type: deviceData[j].type, value: obj.value, name: deviceData[j].name }); } - } + } } return attributes; } - deviceService.getDeviceByName( entityId, @@ -451,8 +450,8 @@ function generateMergePatchActionNgsiLD(req, callback) { callback(error); } else { const attributes = []; - addAttributes(deviceObj.commands, req.body, attributes) - addAttributes(deviceObj.lazy, req.body, attributes) + addAttributes(deviceObj.commands, req.body, attributes); + addAttributes(deviceObj.lazy, req.body, attributes); const executeMergePatchHandler = apply( contextServerUtils.mergePatchHandler, entityId, @@ -461,10 +460,7 @@ function generateMergePatchActionNgsiLD(req, callback) { contextServerUtils.getLDPath(req), attributes ); - async.waterfall( - [executeMergePatchHandler], - callback() - ); + async.waterfall([executeMergePatchHandler], callback()); } } ); @@ -477,7 +473,6 @@ function generateMergePatchActionNgsiLD(req, callback) { * @param {Object} res Response that will be sent. */ function handleMergePatchNgsiLD(req, res, next) { - function handleMergePatchRequest(error, result) { if (error) { logger.debug(context, 'There was an error handling the merge-patch: %s.', error); @@ -492,15 +487,15 @@ function handleMergePatchNgsiLD(req, res, next) { if ((req.is('json') || req.is('application/ld+json')) === false) { return handleMergePatchRequest(new errors.UnsupportedContentType(req.header('content-type'))); } - + if (req.body) { logger.debug(context, JSON.stringify(req.body, null, 4)); } - if (contextServerUtils.mergePatchHandler){ + if (contextServerUtils.mergePatchHandler) { generateMergePatchActionNgsiLD(req, handleMergePatchRequest); } else { - return handleMergePatchRequest(new errors.MethodNotSupported(req.method, req.path)) + return handleMergePatchRequest(new errors.MethodNotSupported(req.method, req.path)); } } @@ -610,7 +605,7 @@ function handleQueryNgsiLD(req, res, next) { getFunction(function handleFindDevice(error, innerDevice) { let deviceList = []; if (!innerDevice) { - return callback(new errors.DeviceNotFound(contextEntity.id)); + return callback(new errors.DeviceNotFound(contextEntity.id, contextEntity)); } if (innerDevice.count) { @@ -689,7 +684,7 @@ function ErrorHandlingNgsiLD(action) { error: error.name, description: error.message.replace(/[<>\"\'=;\(\)]/g, '') }); - } + }; } /** @@ -812,7 +807,7 @@ function loadUnsupportedEndpointsNGSILD(router) { function loadContextRoutesNGSILD(router) { // In a more evolved implementation, more endpoints could be added to queryPathsNgsi2 // according to https://www.etsi.org/standards-search#page=1&search=GS%20CIM%20009 - + const support = config.getConfig().server.ldSupport; let i; @@ -832,7 +827,7 @@ function loadContextRoutesNGSILD(router) { router.patch('/ngsi-ld/v1/entities/:entity', [ preprocessNGSILD, validateNGSILD(support.null, support.datasetId), - handleMergePatchNgsiLD, + handleMergePatchNgsiLD, ErrorHandlingNgsiLD('Merge-Patch') ]); diff --git a/lib/services/northBound/contextServer-NGSI-v2.js b/lib/services/northBound/contextServer-NGSI-v2.js index 2585b998f..9f3db67fb 100644 --- a/lib/services/northBound/contextServer-NGSI-v2.js +++ b/lib/services/northBound/contextServer-NGSI-v2.js @@ -520,7 +520,7 @@ function handleQueryNgsi2(req, res, next) { getFunction(function handleFindDevice(error, innerDevice) { let deviceList = []; if (!innerDevice) { - return callback(new errors.DeviceNotFound(contextEntity.id)); + return callback(new errors.DeviceNotFound(contextEntity.id), contextEntity); } if (innerDevice.count) { diff --git a/lib/services/northBound/deviceProvisioningServer.js b/lib/services/northBound/deviceProvisioningServer.js index d599b2758..c9b3ccb07 100644 --- a/lib/services/northBound/deviceProvisioningServer.js +++ b/lib/services/northBound/deviceProvisioningServer.js @@ -263,7 +263,11 @@ function handleGetDevice(req, res, next) { } else if (device) { res.status(200).json(toProvisioningAPIFormat(device)); } else { - next(new errors.DeviceNotFound(req.params.deviceId)); + next(new errors.DeviceNotFound(req.params.deviceId), { + apikey: req.query.apikey, + service: req.headers['fiware-service'], + subservice: req.headers['fiware-servicepath'] + }); } } ); @@ -276,7 +280,13 @@ function getDevice(deviceId, apikey, service, subservice, callback) { } else if (device) { callback(null, device); } else { - callback(new errors.DeviceNotFound(deviceId)); + callback( + new errors.DeviceNotFound(deviceId, { + apikey: apikey, + service: service, + subservice: subservice + }) + ); } }); } @@ -320,7 +330,13 @@ function handleRemoveDevice(req, res, next) { if (error && error.code !== 404) { next(error); } else if (error && error.code === 404) { - next(new errors.DeviceNotFound(req.params.deviceId)); + next( + new errors.DeviceNotFound(req.params.deviceId, { + apikey: req.query.apikey, + service: req.headers['fiware-service'], + subservice: req.headers['fiware-servicepath'] + }) + ); } else { res.status(204).send(); } @@ -359,7 +375,13 @@ function handleRemoveDevices(req, res, next) { if (error && error.code !== 404) { theError = !theError ? error : theError; } else if (error && error.code === 404) { - theError = !theError ? new errors.DeviceNotFound(devicetoRemove.deviceId) : theError; + theError = !theError + ? new errors.DeviceNotFound(devicetoRemove.deviceId, { + apikey: devicetoRemove.apikey, + service: req.headers['fiware-service'], + subservice: req.headers['fiware-servicepath'] + }) + : theError; } } ); // waterfall @@ -423,7 +445,13 @@ function handleUpdateDevice(req, res, next) { } ); } else { - next(new errors.DeviceNotFound(req.params.deviceId)); + next( + new errors.DeviceNotFound(req.params.deviceId, { + apikey: req.query.apikey, + service: req.headers['fiware-service'], + subservice: req.headers['fiware-servicepath'] + }) + ); } } ); diff --git a/lib/services/northBound/restUtils.js b/lib/services/northBound/restUtils.js index 9be885c80..46dc27b38 100644 --- a/lib/services/northBound/restUtils.js +++ b/lib/services/northBound/restUtils.js @@ -65,7 +65,7 @@ function checkMandatoryQueryParams(mandatoryAttributes, body, callback) { } if (missing.length !== 0) { - const error = new errors.MissingAttributes('Missing attributes: ' + JSON.stringify(missing)); + const error = new errors.MissingAttributes('Missing attributes: ' + JSON.stringify(missing), body); error.code = '400'; callback(error); diff --git a/package.json b/package.json index b30aaaff8..37e08fe00 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "iotagent-node-lib", "license": "AGPL-3.0-only", "description": "IoT Agent library to interface with NGSI Context Broker", - "version": "4.2.0-next", + "version": "4.3.0-next", "homepage": "https://github.com/telefonicaid/iotagent-node-lib", "keywords": [ "fiware", diff --git a/test/unit/ngsi-ld/ngsiService/active-devices-test.js b/test/unit/ngsi-ld/ngsiService/active-devices-test.js index 151ce0e4b..b799af782 100644 --- a/test/unit/ngsi-ld/ngsiService/active-devices-test.js +++ b/test/unit/ngsi-ld/ngsiService/active-devices-test.js @@ -600,7 +600,6 @@ describe('NGSI-LD - Active attributes test', function () { should.exist(error.name); error.code.should.equal(207); error.details.notUpdated.should.equal('someEntities'); - error.message.should.equal('Error accesing entity data for device: light1 of type: Light'); error.name.should.equal('ENTITY_GENERIC_ERROR'); contextBrokerMock.done(); done();