diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 139597f9..9873dae3 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1,2 +1 @@ - - +- ADD: check and usage endpoint and transport from Group level when commands diff --git a/docs/usermanual.md b/docs/usermanual.md index 8371cec3..51ea32a3 100644 --- a/docs/usermanual.md +++ b/docs/usermanual.md @@ -149,60 +149,59 @@ then the NGSI v2 update uses `10`(number), `true` (boolean) and `78.8` (number) (string) and "78.8" (string). This functionality relies on string measures casting feature implemented in the iotagent library. This functionality -uses native JavaScript [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) -function to cast data coming from measures (as text) to JSON native types. This functionality does not change the attribute type, -using the type specified in the config group or device provision, even if it is not consistent with the measures that are coming. - +uses native JavaScript +[`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) function +to cast data coming from measures (as text) to JSON native types. This functionality does not change the attribute type, +using the type specified in the config group or device provision, even if it is not consistent with the measures that +are coming. As an example, for a given measure: - + ``` -a|1|b|1.01|c|true|d|null|e|[1,2,3]|f|['a','b','c']|g|{a:1,b:2,c:3}|h|I'm a string +a|1|b|1.01|c|true|d|null|e|[1,2,3]|f|['a','b','c']|g|{a:1,b:2,c:3}|h|I'm a string ``` The resulting entity would be something like: ```json { - "id": "entityid:001", - "type": "entitytype", - "a": { - "type": "provisionedType", - "value": 1 - }, - "b": { - "type": "provisionedType", - "value": 1.01 - }, - "c": { - "type": "provisionedType", - "value": true - }, - "d": { - "type": "provisionedType", - "value": null - }, - "e": { - "type": "provisionedType", - "value": [1,2,3] - }, - "f": { - "type": "provisionedType", - "value": ["a","b","c"] - }, - "g": { - "type": "provisionedType", - "value": {"a":1,"b":2,"c":3} - }, - "h": { - "type": "provisionedType", - "value": "I'm a string" - } + "id": "entityid:001", + "type": "entitytype", + "a": { + "type": "provisionedType", + "value": 1 + }, + "b": { + "type": "provisionedType", + "value": 1.01 + }, + "c": { + "type": "provisionedType", + "value": true + }, + "d": { + "type": "provisionedType", + "value": null + }, + "e": { + "type": "provisionedType", + "value": [1, 2, 3] + }, + "f": { + "type": "provisionedType", + "value": ["a", "b", "c"] + }, + "g": { + "type": "provisionedType", + "value": { "a": 1, "b": 2, "c": 3 } + }, + "h": { + "type": "provisionedType", + "value": "I'm a string" + } } ``` - -Note that `provisionedType` is the type included in the device provision or config group, and it is not changed. - +Note that `provisionedType` is the type included in the device provision or config group, and it is not changed. ### Transport Protocol @@ -247,8 +246,8 @@ and [Practice: Scenario 3: commands - error](https://github.com/telefonicaid/iotagent-node-lib/blob/master/doc/northboundinteractions.md#scenario-3-commands-error). MQTT devices commands are always push. For HTTP Devices commands to be push they **must** be provisioned with the -`endpoint` attribute, that will contain the URL where the IoT Agent will send the received commands. Otherwise the -command will be poll. When using the HTTP transport, the command handling have two flavours: +`endpoint` attribute, from device or group device, that will contain the URL where the IoT Agent will send the received +commands. Otherwise the command will be poll. When using the HTTP transport, the command handling have two flavours: - **Push commands**: The request payload format will be the one described in the UL Protocol description. The device will reply with a 200OK response containing the result of the command in the UL2.0 result format. Example of the @@ -315,19 +314,16 @@ by the protocol, in this case '/ul', just include apikey and deviceid (e.g: `/FF > **Note** Measures and commands are sent over different MQTT topics: > -> * _Measures_ are sent on the `////attrs` topic, -> * _Commands_ are sent on the `///cmd` topic, +> - _Measures_ are sent on the `////attrs` topic, +> - _Commands_ are sent on the `///cmd` topic, > -> The reasoning behind this is that when sending measures northbound from device to IoT Agent, -> it is necessary to explicitly identify which IoT Agent is needed to parse the data. This -> is done by prefixing the relevant MQTT topic with a protocol, otherwise there is no way to -> define which agent is processing the measure. This mechanism allows smart systems to connect -> different devices to different IoT Agents according to need. +> The reasoning behind this is that when sending measures northbound from device to IoT Agent, it is necessary to +> explicitly identify which IoT Agent is needed to parse the data. This is done by prefixing the relevant MQTT topic +> with a protocol, otherwise there is no way to define which agent is processing the measure. This mechanism allows +> smart systems to connect different devices to different IoT Agents according to need. > -> For southbound commands, this distinction is unnecessary since the correct IoT Agent has already -> registered itself for the command during the device provisioning step and the device will always -> receive commands in an appropriate format. - +> For southbound commands, this distinction is unnecessary since the correct IoT Agent has already registered itself for +> the command during the device provisioning step and the device will always receive commands in an appropriate format. This transport protocol binding is still under development. @@ -375,7 +371,8 @@ commands and a topic to receive configuration information. This mechanism can be configuration flag, `configRetrieval`. In case of MQTT to retrieve configuration parameters from the Context Broker, it is required that the device should be -provisioned using "MQTT" as transport key. By default it will be considered "HTTP" as transport. +provisioned using "MQTT" as transport key, at device or group level. By default it will be considered "HTTP" as +transport. The parameter will be given as follows: diff --git a/lib/bindings/HTTPBindings.js b/lib/bindings/HTTPBindings.js index 13e1a079..115df2a8 100644 --- a/lib/bindings/HTTPBindings.js +++ b/lib/bindings/HTTPBindings.js @@ -45,7 +45,6 @@ const config = require('../configService'); let context = { op: 'IOTAUL.HTTP.Binding' }; -const transport = 'HTTP'; /* eslint-disable-next-line no-unused-vars */ function handleError(error, req, res, next) { @@ -67,7 +66,7 @@ function parseData(req, res, next) { let data; let error; let payload; - + context = fillService(context, { service: 'n/a', subservice: 'n/a' }); if (req.body) { payload = req.body; } else { @@ -264,7 +263,7 @@ function handleIncomingMeasure(req, res, next) { } } - utils.retrieveDevice(req.deviceId, req.apiKey, /*transport,*/ processDeviceMeasure); + utils.retrieveDevice(req.deviceId, req.apiKey, processDeviceMeasure); } /** @@ -278,6 +277,7 @@ function handleIncomingMeasure(req, res, next) { function generateCommandExecution(apiKey, device, attribute) { const cmdName = attribute.name; const cmdAttributes = attribute.value; + context = fillService(context, device); const options = { url: device.endpoint, method: 'POST', @@ -360,6 +360,7 @@ function generateCommandExecution(apiKey, device, attribute) { * @param {String} attributes Command attributes (in NGSIv1 format). */ function commandHandler(device, attributes, callback) { + context = fillService(context, device); utils.getEffectiveApiKey(device.service, device.subservice, device, function (error, apiKey) { async.series(attributes.map(generateCommandExecution.bind(null, apiKey, device)), function (error) { if (error) { @@ -404,6 +405,7 @@ function addDefaultHeader(req, res, next) { * @param {Object} device Device object containing all the information about the provisioned device. */ function setPollingAndDefaultTransport(device, group, callback) { + context = fillService(context, device); config.getLogger().debug(context, 'httpbinding.setPollingAndDefaultTransport device %j group %j', device, group); if (!device.transport) { device.transport = group && group.transport ? group.transport : 'HTTP'; @@ -426,6 +428,7 @@ function setPollingAndDefaultTransport(device, group, callback) { * @param {Object} device Device object containing all the information about the provisioned device. */ function deviceProvisioningHandler(device, callback) { + context = fillService(context, device); config.getLogger().debug(context, 'httpbinding.deviceProvisioningHandler %j', device); let group = {}; iotAgentLib.getConfigurationSilently(config.getConfig().iota.defaultResource || '', device.apikey, function ( @@ -446,6 +449,7 @@ function deviceProvisioningHandler(device, callback) { * @param {Object} device Device object containing all the information about the updated device. */ function deviceUpdatingHandler(device, callback) { + context = fillService(context, device); config.getLogger().debug(context, 'httpbinding.deviceUpdatingHandler %j', device); let group = {}; iotAgentLib.getConfigurationSilently(config.getConfig().iota.defaultResource || '', device.apikey, function ( diff --git a/lib/commonBindings.js b/lib/commonBindings.js index d40a9502..23fffa28 100644 --- a/lib/commonBindings.js +++ b/lib/commonBindings.js @@ -262,7 +262,7 @@ function singleMeasure(apiKey, attribute, device, messageStr, message) { * @param {String} topic Topic of the form: '//deviceId/attributes[/]'. * @param {Object} message message body (Object or Buffer, depending on the value). */ -function messageHandler(topic, message /*, protocol*/) { +function messageHandler(topic, message) { if (topic[0] !== '/') { topic = '/' + topic; } @@ -329,7 +329,7 @@ function messageHandler(topic, message /*, protocol*/) { } } - utils.retrieveDevice(deviceId, apiKey, /*protocol,*/ processDeviceMeasure); + utils.retrieveDevice(deviceId, apiKey, processDeviceMeasure); } /** @@ -341,7 +341,7 @@ function messageHandler(topic, message /*, protocol*/) { */ function amqpMessageHandler(topic, message) { regenerateTransid(topic); - messageHandler(topic, message /*, 'AMQP'*/); + messageHandler(topic, message); } /** @@ -354,7 +354,7 @@ function amqpMessageHandler(topic, message) { function mqttMessageHandler(topic, message) { regenerateTransid(topic); config.getLogger().debug(context, 'message topic: %s', topic); - messageHandler(topic, message /*, 'MQTT'*/); + messageHandler(topic, message); } exports.amqpMessageHandler = amqpMessageHandler; diff --git a/lib/iotaUtils.js b/lib/iotaUtils.js index f20d229a..38d2490a 100644 --- a/lib/iotaUtils.js +++ b/lib/iotaUtils.js @@ -62,76 +62,6 @@ function getEffectiveApiKey(service, subservice, device, callback) { } } -// function findOrCreate(deviceId, transport, apikey, group, callback) { -// iotAgentLib.getDeviceSilently(deviceId, apikey, group.service, group.subservice, function (error, device) { -// if (!error && device) { -// if ( -// (!('apikey' in device) || device.apikey === undefined) && -// 'apikey' in group && -// group.apikey !== undefined -// ) { -// config -// .getLogger() -// .info(context, 'Update provisioned device %j with measure/group apikey %j', device, group.apikey); -// device.apikey = group.apikey; // group apikey is the same of current measure apikey -// iotAgentLib.updateDevice(device, function (error) { -// callback(error, device, group); -// }); -// } else { -// callback(null, device, group); -// } -// } else if (error.name === 'DEVICE_NOT_FOUND') { -// const newDevice = { -// id: deviceId, -// service: group.service, -// subservice: group.subservice, -// type: group.type -// }; - -// if ( -// config.getConfig().iota && -// config.getConfig().iota.iotManager && -// config.getConfig().iota.iotManager.protocol -// ) { -// newDevice.protocol = config.getConfig().iota.iotManager.protocol; -// } - -// // Fix transport depending on binding -// if (!newDevice.transport) { -// newDevice.transport = transport; -// } -// if ('ngsiVersion' in group && group.ngsiVersion !== undefined) { -// newDevice.ngsiVersion = group.ngsiVersion; -// } -// if ( -// (!('apikey' in newDevice) || newDevice.apikey === undefined) && -// 'apikey' in group && -// group.apikey !== undefined -// ) { -// newDevice.apikey = group.apikey; -// } -// // Check autoprovision flag in order to register or not device -// if (group.autoprovision === undefined || group.autoprovision === true) { -// iotAgentLib.register(newDevice, function (error, device) { -// callback(error, device, group); -// }); -// } else { -// config -// .getLogger() -// .info( -// context, -// 'Device %j not provisioned due autoprovision is disabled by its conf %j', -// newDevice, -// group -// ); -// callback(new errors.DeviceNotFound(deviceId)); -// } -// } else { -// callback(error); -// } -// }); -// } - /** * Retrieve a device from the device repository based on the given APIKey and DeviceID, creating one if none is * found for the given data. @@ -139,7 +69,7 @@ function getEffectiveApiKey(service, subservice, device, callback) { * @param {String} deviceId Device ID of the device that wants to be retrieved or created. * @param {String} apiKey APIKey of the Device Group (or default APIKey). */ -function retrieveDevice(deviceId, apiKey, /*transport,*/ callback) { +function retrieveDevice(deviceId, apiKey, callback) { if (apiKey === config.getConfig().defaultKey) { iotAgentLib.getDevicesByAttribute('id', deviceId, undefined, undefined, function (error, devices) { if (error) { @@ -163,7 +93,7 @@ function retrieveDevice(deviceId, apiKey, /*transport,*/ callback) { async.waterfall( [ apply(iotAgentLib.getConfigurationSilently, config.getConfig().iota.defaultResource, apiKey), - apply(iotAgentLib.findOrCreate, deviceId, /* transport,*/ apiKey), // group.apikey and apikey are the same + apply(iotAgentLib.findOrCreate, deviceId, apiKey), // group.apikey and apikey are the same apply( iotAgentLib.mergeDeviceWithConfiguration, ['lazy', 'active', 'staticAttributes', 'commands', 'subscriptions'], diff --git a/lib/iotagent-ul.js b/lib/iotagent-ul.js index bf93a3aa..baa363cd 100644 --- a/lib/iotagent-ul.js +++ b/lib/iotagent-ul.js @@ -97,12 +97,30 @@ function commandHandler(id, type, service, subservice, attributes, callback) { ); callback(error); } else { - transportSelector.applyFunctionFromBinding( - [device, attributes], - 'commandHandler', - device.transport || config.getConfig().defaultTransport, - callback - ); + iotaUtils.getEffectiveApiKey(device.service, device.subservice, device, function (error, apiKey) { + if (error) { + callback(error); + } else { + let group = {}; + iotAgentLib.getConfigurationSilently( + config.getConfig().iota.defaultResource || '', + apiKey, + function (error, foundGroup) { + if (!error) { + group = foundGroup; + } + transportSelector.applyFunctionFromBinding( + [device, attributes], + 'commandHandler', + device.transport || + (group && group.transport ? group.transport : undefined) || + config.getConfig().defaultTransport, + callback + ); + } + ); + } + }); } }); }