Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FIX: Remove measures with attributes name 'id' or 'type' (hotfix 3.4.3) #1483

Merged
merged 15 commits into from
Oct 3, 2023
1 change: 0 additions & 1 deletion CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@

10 changes: 8 additions & 2 deletions doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`)

Expand Down Expand Up @@ -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
Expand Down
14 changes: 13 additions & 1 deletion lib/services/ngsi/entities-NGSI-v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
191 changes: 191 additions & 0 deletions test/unit/ngsiv2/ngsiService/active-devices-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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';
Expand All @@ -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);
Expand Down