diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 8b1378917..e69de29bb 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -1 +0,0 @@ - diff --git a/doc/api.md b/doc/api.md index b1a3d32bd..2e0947fae 100644 --- a/doc/api.md +++ b/doc/api.md @@ -152,7 +152,7 @@ parameters defined at device level in database, the parameters are inherit from ## Entity attributes In the config group/device model there are four list of attributes with different purpose to configure how the -information coming from the device is mapped to the Context Broker attributes: +information coming from the device (measures) is mapped to the Context Broker attributes: - **`attributes`**: Are measures that are pushed from the device to the IoT agent. This measure changes will be sent to the Context Broker as updateContext requests over the device entity. NGSI queries to the context broker will be @@ -179,7 +179,9 @@ information coming from the device is mapped to the Context Broker attributes: All of them have the same syntax, a list of objects with the following attributes: - **object_id** (optional): name of the attribute as coming from the device. -- **name** (mandatory): ID of the attribute in the target entity in the Context Broker. +- **name** (mandatory): ID of the attribute in the target entity in the Context Broker. Note that `id` and `type` + are not valid attribute names at Context Broker. Thus, although a measure named `id` or `type` will not break the IOT Agent, they + are silently ignored and never progress toward Context Broker entities. - **type** (mandatory): name of the type of the attribute in the target entity. - **metadata** (optional): additional static metadata for the attribute in the target entity. (e.g. `unitCode`) @@ -209,6 +211,10 @@ Additionally for commands (which are attributes of type `command`) the following - **contentType**: `content-type` header used when send command by HTTP transport (ignored in other kinds of transports) +Note that, when information comming from devices, this means measures, are not defined neither in the group, nor in the +device, the IoT agent will store that information into the destination entity using the same attribute name than the +measure name, unless `explicitAttrs` is defined. Measures `id` or `type` names are invalid, and will be ignored. + ## Multientity support The IOTA is able to persists measures coming from a single device to more than one entity, declaring the target entities diff --git a/lib/services/ngsi/entities-NGSI-v2.js b/lib/services/ngsi/entities-NGSI-v2.js index 38fa0c36f..2c7b27d6c 100644 --- a/lib/services/ngsi/entities-NGSI-v2.js +++ b/lib/services/ngsi/entities-NGSI-v2.js @@ -340,6 +340,16 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca attributes, typeInformation ); + // if any measure has name 'id' or 'type' it should be removed + // (as they cannot be progressed as attribute names, given that 'id' or 'type' are forbidden names for attributes in CB) + var attributesWithoutIdType = []; + attributes.forEach(function (attribute) { + if (attribute.name !== 'id' && attribute.name !== 'type') { + attributesWithoutIdType.push(attribute); + } + }); + attributes = attributesWithoutIdType; + const payload = { entities: [ { @@ -729,7 +739,9 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca attr, newAttr ); - delete payload.entities[0][attr.object_id]; + if (!['id', 'type'].includes(attr.object_id)) { + delete payload.entities[0][attr.object_id]; + } attr = undefined; // stop processing attr newAttr = undefined; } else { diff --git a/package.json b/package.json index 6ebd2463d..8a6c49b3f 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": "3.4.3", + "version": "3.4.4", "homepage": "https://github.com/telefonicaid/iotagent-node-lib", "keywords": [ "fiware", diff --git a/test/unit/ngsiv2/ngsiService/active-devices-test.js b/test/unit/ngsiv2/ngsiService/active-devices-test.js index 8a59af89e..352a5cd5f 100644 --- a/test/unit/ngsiv2/ngsiService/active-devices-test.js +++ b/test/unit/ngsiv2/ngsiService/active-devices-test.js @@ -156,6 +156,52 @@ const iotAgentConfig = { } } ] + }, + StupidDevice: { + type: 'StupidDevice', + commands: [], + lazy: [], + staticAttributes: [], + active: [ + { + name: 'type', + object_id: 't', + type: 'text' + }, + { + name: 'id', + object_id: 'i', + type: 'text' + }, + { + name: 'meas', + object_id: 'm', + type: 'String' + } + ] + }, + StupidDevice2: { + type: 'StupidDevice2', + commands: [], + lazy: [], + staticAttributes: [], + active: [ + { + name: 'type', + object_id: 'type', + type: 'text' + }, + { + name: 'id', + object_id: 'id', + type: 'text' + }, + { + name: 'meas', + object_id: 'meas', + type: 'String' + } + ] } }, service: 'smartgondor', @@ -856,6 +902,150 @@ describe('NGSI-v2 - Active attributes test', function () { }); }); + describe('When the IoT Agent receives autoprovisioned id and type measures', function () { + const valuesIdType = [ + { + name: 'id', + type: 'text', + value: 'idIoTA' + }, + { + name: 'type', + type: 'text', + value: 'typeIoTA' + }, + { + name: 'm', + type: 'text', + value: 'measIoTA' + } + ]; + + beforeEach(function (done) { + + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', 'gardens') + .post('/v2/entities?options=upsert', { + id: 'stupiddevice1', + type: 'StupidDevice', + meas: { + value: 'measIoTA', + type: 'String' + } + }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should not affect to the real ID and Type to store in the context broker', function (done) { + iotAgentLib.update('stupiddevice1', 'StupidDevice', '', valuesIdType, function (error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + }); + + describe('When the IoT Agent receives provisioned id and type measures with different object_id names', function () { + const valuesIdType2 = [ + { + name: 'i', + type: 'text', + value: 'idIoTA2' + }, + { + name: 't', + type: 'text', + value: 'typeIoTA2' + }, + { + name: 'm', + type: 'text', + value: 'measIoTA2' + } + ]; + + beforeEach(function (done) { + + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', 'gardens') + .post('/v2/entities?options=upsert', { + id: 'stupiddevice2', + type: 'StupidDevice', + meas: { + value: 'measIoTA2', + type: 'String' + } + }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should not affect to the real ID and Type to store in the context broker', function (done) { + iotAgentLib.update('stupiddevice2', 'StupidDevice', '', valuesIdType2, function (error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + }); + + describe('When the IoT Agent receives provisioned id and type measures with the same object_id name', function () { + const valuesIdType3 = [ + { + name: 'id', + type: 'text', + value: 'idIoTA' + }, + { + name: 'type', + type: 'text', + value: 'typeIoTA' + }, + { + name: 'meas', + type: 'text', + value: 'measIoTA' + } + ]; + + beforeEach(function (done) { + + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', 'gardens') + .post('/v2/entities?options=upsert', { + id: 'stupiddevice3', + type: 'StupidDevice2', + meas: { + value: 'measIoTA', + type: 'String' + } + }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should not affect to the real ID and Type to store in the context broker', function (done) { + iotAgentLib.update('stupiddevice3', 'StupidDevice2', '', valuesIdType3, function (error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + }); + describe('When the IoT Agent receives new information from a device and CBis defined using environment variables', function () { beforeEach(function (done) { process.env.IOTA_CB_HOST = 'cbhost'; @@ -873,6 +1063,7 @@ describe('NGSI-v2 - Active attributes test', function () { iotAgentLib.activate(iotAgentConfig, done); }); + it('should change the value of the corresponding attribute in the context broker', function (done) { iotAgentLib.update('light1', 'Light', '', values, function (error) { should.not.exist(error);