From c42cebef0d3dd755229daa394fa7240045c714df Mon Sep 17 00:00:00 2001 From: mapedraza Date: Fri, 23 Aug 2024 15:58:42 +0200 Subject: [PATCH 1/7] Code + tests --- lib/constants.js | 3 + .../deviceGroupAdministrationServer.js | 58 +- .../multipleConfigGroupsCreation.json | 44 + .../provisionDuplicateConfigGroup.json | 35 + .../provisionFullConfigGroup.json | 36 + .../provisionFullConfigGroupAlternate.json | 36 + test/unit/general/deviceService-test.js | 102 ++ .../mongodb-configGroup-registry-test.js | 452 +++++++ .../mongodb/mongodb-group-registry-test.js | 67 +- .../mongodb/mongodb-service-registry-test.js | 477 +++++++ .../unit/ngsiv2/general/deviceService-test.js | 95 +- .../general/iotam-autoregistration-test.js | 195 +++ .../provisioning/device-group-api-test.js | 99 ++ ...evice-provisioning-configGroup-api_test.js | 1189 +++++++++++++++++ 14 files changed, 2848 insertions(+), 40 deletions(-) create mode 100644 test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json create mode 100644 test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json create mode 100644 test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json create mode 100644 test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json create mode 100644 test/unit/mongodb/mongodb-configGroup-registry-test.js create mode 100644 test/unit/mongodb/mongodb-service-registry-test.js create mode 100644 test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js diff --git a/lib/constants.js b/lib/constants.js index d1d2891e5..03e35d3df 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -88,5 +88,8 @@ module.exports = { ATTRIBUTE_DEFAULT, DATETIME_DEFAULT, + CONFIGGROUP_TERM: 'groups', + CONFIGGROUP_API_PATH: '/iot/groups', + getInitialValueForType }; diff --git a/lib/services/northBound/deviceGroupAdministrationServer.js b/lib/services/northBound/deviceGroupAdministrationServer.js index 40829394f..7cd9f7c73 100644 --- a/lib/services/northBound/deviceGroupAdministrationServer.js +++ b/lib/services/northBound/deviceGroupAdministrationServer.js @@ -29,13 +29,19 @@ const restUtils = require('./restUtils'); const groupService = require('./../groups/groupService'); const async = require('async'); const apply = async.apply; -const templateGroup = require('../../templates/deviceGroup.json'); +const templateGroupService = require('../../templates/deviceGroup.json'); let configurationHandler; let removeConfigurationHandler; let configurationMiddleware = []; const _ = require('underscore'); const mandatoryHeaders = ['fiware-service', 'fiware-servicepath']; const mandatoryParameters = ['resource', 'apikey']; +const constants = require('../../constants'); + +// Create new templeate for configuration groups replacing services by CONFIGGROUP_TERM +const templateGroup = JSON.parse(JSON.stringify(templateGroupService)); +templateGroup.properties[constants.CONFIGGROUP_TERM] = templateGroup.properties.services; +delete templateGroup.properties.services; const apiToInternal = { entity_type: 'type', @@ -118,6 +124,8 @@ function applyConfigurationMiddlewares(newConfiguration, callback) { * @param {Function} next Invokes the next middleware in the chain. */ function handleCreateDeviceGroup(req, res, next) { + req.body = checkAndModifyGroupRecv(req._parsedUrl.pathname, req.body); + for (let i = 0; i < req.body.services.length; i++) { req.body.services[i] = applyMap(apiToInternal, req.body.services[i]); req.body.services[i].service = req.headers['fiware-service']; @@ -161,8 +169,7 @@ function handleListDeviceGroups(req, res, next) { } else { translatedGroup = applyMap(internalToApi, group); } - - res.status(200).send(translatedGroup); + res.status(200).send(checkAndModifyGroupResp(req._parsedUrl.pathname, translatedGroup)); } }; @@ -267,21 +274,32 @@ function loadContextRoutes(router) { router.post( '/iot/services', restUtils.checkRequestAttributes('headers', mandatoryHeaders), + restUtils.checkBody(templateGroupService), + handleCreateDeviceGroup + ); + + router.post( + '/iot/groups', + restUtils.checkRequestAttributes('headers', mandatoryHeaders), restUtils.checkBody(templateGroup), handleCreateDeviceGroup ); - router.get('/iot/services', restUtils.checkRequestAttributes('headers', mandatoryHeaders), handleListDeviceGroups); + router.get( + ['/iot/services', '/iot/groups'], + restUtils.checkRequestAttributes('headers', mandatoryHeaders), + handleListDeviceGroups + ); router.put( - '/iot/services', + ['/iot/services', '/iot/groups'], restUtils.checkRequestAttributes('headers', mandatoryHeaders), restUtils.checkRequestAttributes('query', mandatoryParameters), handleModifyDeviceGroups ); router.delete( - '/iot/services', + ['/iot/services', '/iot/groups'], restUtils.checkRequestAttributes('headers', mandatoryHeaders), restUtils.checkRequestAttributes('query', mandatoryParameters), handleDeleteDeviceGroups @@ -306,6 +324,34 @@ function clear(callback) { callback(); } +function renameObjetKeys(keysMap, obj) { + return Object.keys(obj).reduce((acc, key) => { + // Si la clave existe en el mapa, usar la clave renombrada + const renamedKey = keysMap[key] || key; + + // Agregar la clave renombrada al acumulador + acc[renamedKey] = obj[key]; + + return acc; + }, {}); +} + +function checkAndModifyGroupResp(route, payload) { + if (route === '/iot/services') { + return payload; + } else { + return renameObjetKeys({ services: 'groups' }, payload); + } +} + +function checkAndModifyGroupRecv(route, payload) { + if (route === '/iot/services') { + return payload; + } else { + return renameObjetKeys({ groups: 'services' }, payload); + } +} + exports.loadContextRoutes = loadContextRoutes; exports.setConfigurationHandler = setConfigurationHandler; exports.setRemoveConfigurationHandler = setRemoveConfigurationHandler; diff --git a/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json b/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json new file mode 100644 index 000000000..64b7748e7 --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json @@ -0,0 +1,44 @@ +{ + "groups": [ + { + "resource": "/deviceTest", + "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732", + "entity_type": "Light", + "trust": "8970A9078A803H3BL98PINEQRW8342HBAMS", + "cbHost": "http://unexistentHost:1026", + "commands": [ + { + "name": "wheel1", + "type": "Wheel" + } + ], + "lazy": [ + { + "name": "luminescence", + "type": "Lumens" + } + ], + "attributes": [ + { + "name": "status", + "type": "Boolean" + } + ] + }, + { + "resource": "/deviceTest", + "apikey": "23HJK3Y9090DSFL173209HV8801232", + "entity_type": "Termometer", + "trust": "BL9803H3QRW8342HBAMS8A8", + "cbHost": "http://unexistentHost:1026", + "commands": [ + { + "name": "temperature", + "type": "degrees" + } + ], + "lazy": [], + "attributes": [] + } + ] +} diff --git a/test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json b/test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json new file mode 100644 index 000000000..d87fdbc7e --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json @@ -0,0 +1,35 @@ +{ + "groups": [ + { + "resource": "/deviceDuplicateGroup", + "apikey": "JK09H3L12K09H3L123HJK0732L123HJK38K09H3", + "entity_type": "CommandMachinery", + "trust": "8970A9078A803H3BL98PINEQRW8342HBAMS", + "commands": [ + { + "name": "wheel1", + "type": "Wheel" + } + ], + "lazy": [ + { + "name": "luminescence", + "type": "Lumens" + } + ], + "attributes": [ + { + "name": "status", + "type": "Boolean" + } + ], + "static_attributes": [ + { + "name": "bootstrapServer", + "type": "Address", + "value": "127.0.0.1" + } + ] + } + ] +} diff --git a/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json b/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json new file mode 100644 index 000000000..5764460b5 --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json @@ -0,0 +1,36 @@ +{ + "groups": [ + { + "resource": "/deviceTest", + "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732", + "entity_type": "SensorMachine", + "trust": "8970A9078A803H3BL98PINEQRW8342HBAMS", + "cbHost": "http://unexistentHost:1026", + "commands": [ + { + "name": "wheel1", + "type": "Wheel" + } + ], + "lazy": [ + { + "name": "luminescence", + "type": "Lumens" + } + ], + "attributes": [ + { + "name": "status", + "type": "Boolean" + } + ], + "static_attributes": [ + { + "name": "bootstrapServer", + "type": "Address", + "value": "127.0.0.1" + } + ] + } + ] +} diff --git a/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json b/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json new file mode 100644 index 000000000..530fa0632 --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json @@ -0,0 +1,36 @@ +{ + "groups": [ + { + "resource": "/deviceTest", + "apikey": "754KL23Y9090DSFL123HSFL12380KL23Y2", + "entity_type": "AnotherMachine", + "trust": "8970A9078A803H3BL98PINEQRW8342HBAMS", + "cbHost": "http://unexistentHost:1026", + "commands": [ + { + "name": "wheel1", + "type": "Wheel" + } + ], + "lazy": [ + { + "name": "luminescence", + "type": "Lumens" + } + ], + "attributes": [ + { + "name": "status", + "type": "Boolean" + } + ], + "static_attributes": [ + { + "name": "bootstrapServer", + "type": "Address", + "value": "127.0.0.1" + } + ] + } + ] +} diff --git a/test/unit/general/deviceService-test.js b/test/unit/general/deviceService-test.js index 9b4fb40e8..8078bae31 100644 --- a/test/unit/general/deviceService-test.js +++ b/test/unit/general/deviceService-test.js @@ -153,6 +153,36 @@ const groupCreation = { 'fiware-servicepath': '/testingPath' } }; + +const configGroupCreation = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + entity_type: 'TheLightType', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://192.168.1.1:1026', + commands: [], + lazy: [], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ], + static_attributes: [] + } + ] + }, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } +}; + const deviceCreation = { url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', method: 'POST', @@ -180,6 +210,7 @@ describe('NGSI-v2 - Device Service: utils', function () { async.series([iotAgentLib.clearAll, iotAgentLib.deactivate], done); }); + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When an existing device tries to be retrieved with retrieveOrCreate()', function () { beforeEach(function (done) { contextBrokerMock = nock('http://192.168.1.1:1026') @@ -213,6 +244,41 @@ describe('NGSI-v2 - Device Service: utils', function () { }); }); + describe('When an existing device tries to be retrieved with retrieveOrCreate()', function () { + beforeEach(function (done) { + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/registrations') + .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + + contextBrokerMock + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + async.series( + [request.bind(request, configGroupCreation), request.bind(request, deviceCreation)], + function (error, results) { + done(); + } + ); + }); + + it('should return the existing device', function (done) { + iotAgentLib.retrieveDevice('Light1', '801230BJKL23Y9090DSFL123HJK09H324HV8732', function (error, device) { + should.not.exist(error); + should.exist(device); + + device.id.should.equal('Light1'); + done(); + }); + }); + }); + + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed + describe('When an unexisting device tries to be retrieved for an existing APIKey', function () { beforeEach(function (done) { contextBrokerMock = nock('http://192.168.1.1:1026') @@ -249,6 +315,42 @@ describe('NGSI-v2 - Device Service: utils', function () { }); }); + describe('When an unexisting device tries to be retrieved for an existing APIKey', function () { + beforeEach(function (done) { + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/registrations') + .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + + contextBrokerMock + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + async.series([request.bind(request, configGroupCreation)], function (error, results) { + done(); + }); + }); + + it('should register the device and return it', function (done) { + iotAgentLib.retrieveDevice( + 'UNEXISTENT_DEV', + '801230BJKL23Y9090DSFL123HJK09H324HV8732', + function (error, device) { + should.not.exist(error); + should.exist(device); + + device.id.should.equal('UNEXISTENT_DEV'); + should.exist(device.protocol); + device.protocol.should.equal('MQTT_UL'); + done(); + } + ); + }); + }); + describe('When an unexisting device tries to be retrieved for an unexisting APIKey', function () { it('should raise an error', function (done) { iotAgentLib.retrieveDevice( diff --git a/test/unit/mongodb/mongodb-configGroup-registry-test.js b/test/unit/mongodb/mongodb-configGroup-registry-test.js new file mode 100644 index 000000000..26d1f3d84 --- /dev/null +++ b/test/unit/mongodb/mongodb-configGroup-registry-test.js @@ -0,0 +1,452 @@ +/* + * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U + * + * This file is part of fiware-iotagent-lib + * + * fiware-iotagent-lib is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * fiware-iotagent-lib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with fiware-iotagent-lib. + * If not, see http://www.gnu.org/licenses/. + * + * For those usages not covered by the GNU Affero General Public License + * please contact with::[contacto@tid.es] + * + * Modified by: Daniel Calvo - ATOS Research & Innovation + */ + +/* eslint-disable no-unused-vars */ + +const iotAgentLib = require('../../../lib/fiware-iotagent-lib'); +const _ = require('underscore'); +const utils = require('../../tools/utils'); +const request = utils.request; +const async = require('async'); +const should = require('should'); +const iotAgentConfig = { + logLevel: 'FATAL', + contextBroker: { + host: '192.168.1.1', + port: '1026' + }, + server: { + name: 'testAgent', + port: 4041, + host: 'localhost', + baseRoot: '/' + }, + types: {}, + deviceRegistry: { + type: 'mongodb' + }, + mongodb: { + host: 'localhost', + port: '27017', + db: 'iotagent' + }, + service: 'smartgondor', + subservice: 'gardens', + providerUrl: 'http://smartgondor.com', + deviceRegistrationDuration: 'P1M' +}; +const mongo = require('mongodb').MongoClient; +const mongoUtils = require('./mongoDBUtils'); +const optionsCreation = { + url: 'http://localhost:4041/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + entity_type: 'Light', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://unexistentHost:1026', + commands: [ + { + name: 'wheel1', + type: 'Wheel' + } + ], + lazy: [ + { + name: 'luminescence', + type: 'Lumens' + } + ], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ], + internal_attributes: [ + { + customField: 'customValue' + } + ] + } + ] + }, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } +}; +const optionsDelete = { + url: 'http://localhost:4041/iot/groups', + method: 'DELETE', + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; +const optionsList = { + url: 'http://localhost:4041/iot/groups', + method: 'GET', + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/*' + } +}; +const optionsUpdate = { + url: 'http://localhost:4041/iot/groups', + method: 'PUT', + json: { + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://anotherUnexistentHost:1026', + commands: [ + { + name: 'wheel1', + type: 'Wheel' + } + ], + lazy: [ + { + name: 'luminescence', + type: 'Lumens' + } + ], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ], + static_attributes: [ + { + name: 'bootstrapServer', + type: 'Address', + value: '127.0.0.1' + } + ] + }, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; +const optionsGet = { + url: 'http://localhost:4041/iot/groups', + method: 'GET', + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } +}; +let iotAgentDb; + +describe('MongoDB Group Registry test', function () { + beforeEach(function (done) { + mongoUtils.cleanDbs(function () { + iotAgentLib.activate(iotAgentConfig, function () { + mongo.connect('mongodb://localhost:27017/iotagent', { useNewUrlParser: true }, function (err, db) { + iotAgentDb = db; + done(); + }); + }); + }); + }); + + afterEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentDb.close(function (error) { + mongoUtils.cleanDbs(done); + }); + }); + }); + describe('When a new device group creation request arrives', function () { + it('should store it in the DB', function (done) { + request(optionsCreation, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs); + should.exist(docs.length); + docs.length.should.equal(1); + should.exist(docs[0].type); + should.exist(docs[0].internalAttributes); + should.exist(docs[0].attributes); + should.exist(docs[0].apikey); + docs[0].type.should.equal('Light'); + docs[0].apikey.should.equal('801230BJKL23Y9090DSFL123HJK09H324HV8732'); + docs[0].internalAttributes.length.should.equal(1); + docs[0].internalAttributes[0].customField.should.equal('customValue'); + docs[0].attributes.length.should.equal(1); + docs[0].attributes[0].name.should.equal('status'); + done(); + }); + }); + }); + it('should store the service information from the headers into the DB', function (done) { + request(optionsCreation, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs[0].service); + should.exist(docs[0].subservice); + docs[0].service.should.equal('testservice'); + docs[0].subservice.should.equal('/testingPath'); + done(); + }); + }); + }); + }); + + describe('When a new device group creation request arrives with an existant (apikey, resource) pair', function () { + it('should return a DUPLICATE_GROUP error', function (done) { + request(optionsCreation, function (error, response, body) { + request(optionsCreation, function (error, response, body) { + response.statusCode.should.equal(409); + body.name.should.equal('DUPLICATE_GROUP'); + done(); + }); + }); + }); + }); + + describe('When a device group removal request arrives', function () { + beforeEach(function (done) { + request(optionsCreation, done); + }); + + it('should remove it from the database', function (done) { + request(optionsDelete, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs); + should.exist(docs.length); + docs.length.should.equal(0); + done(); + }); + }); + }); + + it('should return a 204 OK statusCode', function (done) { + request(optionsDelete, function (error, response, body) { + response.statusCode.should.equal(204); + done(); + }); + }); + }); + + describe('When a device group update request arrives', function () { + beforeEach(function (done) { + request(optionsCreation, done); + }); + + it('should update the values in the database', function (done) { + request(optionsUpdate, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs); + should.exist(docs[0].cbHost); + docs[0].cbHost.should.equal('http://anotherUnexistentHost:1026'); + should.exist(docs[0].staticAttributes); + docs[0].staticAttributes.length.should.equal(1); + done(); + }); + }); + }); + }); + + describe('When a multiple device group creation arrives', function () { + const optionsMultipleCreation = _.clone(optionsCreation); + + beforeEach(function (done) { + optionsMultipleCreation.json = utils.readExampleFile( + './test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json' + ); + + done(); + }); + + it('should create the values in the database', function (done) { + request(optionsMultipleCreation, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs); + docs.length.should.equal(2); + done(); + }); + }); + }); + }); + + describe('When a device group listing request arrives', function () { + beforeEach(function (done) { + const optionsCreation1 = _.clone(optionsCreation); + const optionsCreation2 = _.clone(optionsCreation); + const optionsCreation3 = _.clone(optionsCreation); + + optionsCreation2.json = { groups: [] }; + optionsCreation3.json = { groups: [] }; + + optionsCreation2.json.groups[0] = _.clone(optionsCreation.json.groups[0]); + optionsCreation3.json.groups[0] = _.clone(optionsCreation.json.groups[0]); + + optionsCreation2.json.groups[0].apikey = 'qwertyuiop'; + optionsCreation3.json.groups[0].apikey = 'lkjhgfds'; + + async.series( + [ + async.apply(request, optionsCreation1), + async.apply(request, optionsCreation2), + async.apply(request, optionsCreation3) + ], + done + ); + }); + + it('should return all the configured device groups from the database', function (done) { + request(optionsList, function (error, response, body) { + body.count.should.equal(3); + done(); + }); + }); + }); + + describe('When a device group listing arrives with a limit', function () { + const optionsConstrained = { + url: 'http://localhost:4041/iot/groups', + method: 'GET', + qs: { + limit: 3, + offset: 2 + }, + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/*' + } + }; + + beforeEach(function (done) { + const optionsCreationList = []; + const creationFns = []; + + for (let i = 0; i < 10; i++) { + optionsCreationList[i] = _.clone(optionsCreation); + optionsCreationList[i].json = { groups: [] }; + optionsCreationList[i].json.groups[0] = _.clone(optionsCreation.json.groups[0]); + optionsCreationList[i].json.groups[0].apikey = 'qwertyuiop' + i; + creationFns.push(async.apply(request, optionsCreationList[i])); + } + + async.series(creationFns, done); + }); + + it('should return the appropriate count of groups', function (done) { + request(optionsConstrained, function (error, response, body) { + body.count.should.equal(10); + done(); + }); + }); + }); + + describe('When a device info request arrives', function () { + beforeEach(function (done) { + async.series([async.apply(request, optionsCreation)], done); + }); + + it('should return all the configured device groups from the database', function (done) { + request(optionsGet, function (error, response, body) { + should.exist(body); + should.exist(body.count); + body.count.should.equal(1); + should.exist(body.groups); + should.exist(body.groups.length); + body.groups.length.should.equal(1); + body.groups[0].service.should.equal('testservice'); + done(); + }); + }); + }); + + describe('When a device info request arrives and multiple groups have been created', function () { + beforeEach(function (done) { + const optionsCreationList = []; + const creationFns = []; + + for (let i = 0; i < 10; i++) { + optionsCreationList[i] = _.clone(optionsCreation); + optionsCreationList[i].json = { groups: [] }; + optionsCreationList[i].json.groups[0] = _.clone(optionsCreation.json.groups[0]); + optionsCreationList[i].json.groups[0].apikey = 'qwertyuiop' + i; + creationFns.push(async.apply(request, optionsCreationList[i])); + } + + async.series(creationFns, done); + }); + + it('should return all the configured device groups from the database', function (done) { + request(optionsGet, function (error, response, body) { + should.exist(body); + should.exist(body.count); + body.count.should.equal(10); + should.exist(body.groups); + should.exist(body.groups.length); + body.groups.length.should.equal(10); + done(); + }); + }); + }); +}); diff --git a/test/unit/mongodb/mongodb-group-registry-test.js b/test/unit/mongodb/mongodb-group-registry-test.js index bec77d40d..2ca1dd4d9 100644 --- a/test/unit/mongodb/mongodb-group-registry-test.js +++ b/test/unit/mongodb/mongodb-group-registry-test.js @@ -23,6 +23,7 @@ * Modified by: Daniel Calvo - ATOS Research & Innovation */ +// FIXME: parallel tests in mongodb-configGroup-registry-test.js. Remove this file if at the end /iot/services API (now Deprecated) is removed /* eslint-disable no-unused-vars */ const iotAgentLib = require('../../../lib/fiware-iotagent-lib'); @@ -60,10 +61,10 @@ const iotAgentConfig = { const mongo = require('mongodb').MongoClient; const mongoUtils = require('./mongoDBUtils'); const optionsCreation = { - url: 'http://localhost:4041/iot/services', + url: 'http://localhost:4041/iot/groups', method: 'POST', json: { - services: [ + groups: [ { resource: '/deviceTest', apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', @@ -102,7 +103,7 @@ const optionsCreation = { } }; const optionsDelete = { - url: 'http://localhost:4041/iot/services', + url: 'http://localhost:4041/iot/groups', method: 'DELETE', json: {}, headers: { @@ -115,7 +116,7 @@ const optionsDelete = { } }; const optionsList = { - url: 'http://localhost:4041/iot/services', + url: 'http://localhost:4041/iot/groups', method: 'GET', json: {}, headers: { @@ -124,7 +125,7 @@ const optionsList = { } }; const optionsUpdate = { - url: 'http://localhost:4041/iot/services', + url: 'http://localhost:4041/iot/groups', method: 'PUT', json: { apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', @@ -166,7 +167,7 @@ const optionsUpdate = { } }; const optionsGet = { - url: 'http://localhost:4041/iot/services', + url: 'http://localhost:4041/iot/groups', method: 'GET', json: {}, headers: { @@ -176,7 +177,7 @@ const optionsGet = { }; let iotAgentDb; -describe('MongoDB Group Registry test', function () { +describe('MongoDB Group Registry test', function () { beforeEach(function (done) { mongoUtils.cleanDbs(function () { iotAgentLib.activate(iotAgentConfig, function () { @@ -309,7 +310,7 @@ describe('MongoDB Group Registry test', function () { beforeEach(function (done) { optionsMultipleCreation.json = utils.readExampleFile( - './test/unit/examples/groupProvisioningRequests/multipleGroupsCreation.json' + './test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json' ); done(); @@ -337,14 +338,14 @@ describe('MongoDB Group Registry test', function () { const optionsCreation2 = _.clone(optionsCreation); const optionsCreation3 = _.clone(optionsCreation); - optionsCreation2.json = { services: [] }; - optionsCreation3.json = { services: [] }; + optionsCreation2.json = { groups: [] }; + optionsCreation3.json = { groups: [] }; - optionsCreation2.json.services[0] = _.clone(optionsCreation.json.services[0]); - optionsCreation3.json.services[0] = _.clone(optionsCreation.json.services[0]); + optionsCreation2.json.groups[0] = _.clone(optionsCreation.json.groups[0]); + optionsCreation3.json.groups[0] = _.clone(optionsCreation.json.groups[0]); - optionsCreation2.json.services[0].apikey = 'qwertyuiop'; - optionsCreation3.json.services[0].apikey = 'lkjhgfds'; + optionsCreation2.json.groups[0].apikey = 'qwertyuiop'; + optionsCreation3.json.groups[0].apikey = 'lkjhgfds'; async.series( [ @@ -366,7 +367,7 @@ describe('MongoDB Group Registry test', function () { describe('When a device group listing arrives with a limit', function () { const optionsConstrained = { - url: 'http://localhost:4041/iot/services', + url: 'http://localhost:4041/iot/groups', method: 'GET', qs: { limit: 3, @@ -385,9 +386,9 @@ describe('MongoDB Group Registry test', function () { for (let i = 0; i < 10; i++) { optionsCreationList[i] = _.clone(optionsCreation); - optionsCreationList[i].json = { services: [] }; - optionsCreationList[i].json.services[0] = _.clone(optionsCreation.json.services[0]); - optionsCreationList[i].json.services[0].apikey = 'qwertyuiop' + i; + optionsCreationList[i].json = { groups: [] }; + optionsCreationList[i].json.groups[0] = _.clone(optionsCreation.json.groups[0]); + optionsCreationList[i].json.groups[0].apikey = 'qwertyuiop' + i; creationFns.push(async.apply(request, optionsCreationList[i])); } @@ -412,10 +413,10 @@ describe('MongoDB Group Registry test', function () { should.exist(body); should.exist(body.count); body.count.should.equal(1); - should.exist(body.services); - should.exist(body.services.length); - body.services.length.should.equal(1); - body.services[0].service.should.equal('testservice'); + should.exist(body.groups); + should.exist(body.groups.length); + body.groups.length.should.equal(1); + body.groups[0].service.should.equal('testservice'); done(); }); }); @@ -428,9 +429,9 @@ describe('MongoDB Group Registry test', function () { for (let i = 0; i < 10; i++) { optionsCreationList[i] = _.clone(optionsCreation); - optionsCreationList[i].json = { services: [] }; - optionsCreationList[i].json.services[0] = _.clone(optionsCreation.json.services[0]); - optionsCreationList[i].json.services[0].apikey = 'qwertyuiop' + i; + optionsCreationList[i].json = { groups: [] }; + optionsCreationList[i].json.groups[0] = _.clone(optionsCreation.json.groups[0]); + optionsCreationList[i].json.groups[0].apikey = 'qwertyuiop' + i; creationFns.push(async.apply(request, optionsCreationList[i])); } @@ -442,9 +443,9 @@ describe('MongoDB Group Registry test', function () { should.exist(body); should.exist(body.count); body.count.should.equal(10); - should.exist(body.services); - should.exist(body.services.length); - body.services.length.should.equal(10); + should.exist(body.groups); + should.exist(body.groups.length); + body.groups.length.should.equal(10); done(); }); }); @@ -464,11 +465,11 @@ describe('MongoDB Group Registry test', function () { should.exist(body); should.exist(body.count); body.count.should.equal(1); - should.exist(body.services); - should.exist(body.services.length); - body.services.length.should.equal(1); - should.exist(body.services[0].entity_type); - body.services[0].entity_type.should.equal('Light'); + should.exist(body.groups); + should.exist(body.groups.length); + body.groups.length.should.equal(1); + should.exist(body.groups[0].entity_type); + body.groups[0].entity_type.should.equal('Light'); done(); }); }); diff --git a/test/unit/mongodb/mongodb-service-registry-test.js b/test/unit/mongodb/mongodb-service-registry-test.js new file mode 100644 index 000000000..07c1cce8d --- /dev/null +++ b/test/unit/mongodb/mongodb-service-registry-test.js @@ -0,0 +1,477 @@ +/* + * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U + * + * This file is part of fiware-iotagent-lib + * + * fiware-iotagent-lib is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * fiware-iotagent-lib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with fiware-iotagent-lib. + * If not, see http://www.gnu.org/licenses/. + * + * For those usages not covered by the GNU Affero General Public License + * please contact with::[contacto@tid.es] + * + * Modified by: Daniel Calvo - ATOS Research & Innovation + */ + +// FIXME: parallel tests in mongodb-configGroup-registry-test.js. Remove this file if at the end /iot/services API (now Deprecated) is removed +/* eslint-disable no-unused-vars */ + +const iotAgentLib = require('../../../lib/fiware-iotagent-lib'); +const _ = require('underscore'); +const utils = require('../../tools/utils'); +const request = utils.request; +const async = require('async'); +const should = require('should'); +const iotAgentConfig = { + logLevel: 'FATAL', + contextBroker: { + host: '192.168.1.1', + port: '1026' + }, + server: { + name: 'testAgent', + port: 4041, + host: 'localhost', + baseRoot: '/' + }, + types: {}, + deviceRegistry: { + type: 'mongodb' + }, + mongodb: { + host: 'localhost', + port: '27017', + db: 'iotagent' + }, + service: 'smartgondor', + subservice: 'gardens', + providerUrl: 'http://smartgondor.com', + deviceRegistrationDuration: 'P1M' +}; +const mongo = require('mongodb').MongoClient; +const mongoUtils = require('./mongoDBUtils'); +const optionsCreation = { + url: 'http://localhost:4041/iot/services', + method: 'POST', + json: { + services: [ + { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + entity_type: 'Light', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://unexistentHost:1026', + commands: [ + { + name: 'wheel1', + type: 'Wheel' + } + ], + lazy: [ + { + name: 'luminescence', + type: 'Lumens' + } + ], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ], + internal_attributes: [ + { + customField: 'customValue' + } + ] + } + ] + }, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } +}; +const optionsDelete = { + url: 'http://localhost:4041/iot/services', + method: 'DELETE', + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; +const optionsList = { + url: 'http://localhost:4041/iot/services', + method: 'GET', + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/*' + } +}; +const optionsUpdate = { + url: 'http://localhost:4041/iot/services', + method: 'PUT', + json: { + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://anotherUnexistentHost:1026', + commands: [ + { + name: 'wheel1', + type: 'Wheel' + } + ], + lazy: [ + { + name: 'luminescence', + type: 'Lumens' + } + ], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ], + static_attributes: [ + { + name: 'bootstrapServer', + type: 'Address', + value: '127.0.0.1' + } + ] + }, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; +const optionsGet = { + url: 'http://localhost:4041/iot/services', + method: 'GET', + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } +}; +let iotAgentDb; + +describe('MongoDB Service Registry test', function () { + beforeEach(function (done) { + mongoUtils.cleanDbs(function () { + iotAgentLib.activate(iotAgentConfig, function () { + mongo.connect('mongodb://localhost:27017/iotagent', function (err, db) { + iotAgentDb = db; + done(); + }); + }); + }); + }); + + afterEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentDb.close(function (error) { + mongoUtils.cleanDbs(done); + }); + }); + }); + describe('When a new device group creation request arrives', function () { + it('should store it in the DB', function (done) { + request(optionsCreation, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs); + should.exist(docs.length); + docs.length.should.equal(1); + should.exist(docs[0].type); + should.exist(docs[0].internalAttributes); + should.exist(docs[0].attributes); + should.exist(docs[0].apikey); + docs[0].type.should.equal('Light'); + docs[0].apikey.should.equal('801230BJKL23Y9090DSFL123HJK09H324HV8732'); + docs[0].internalAttributes.length.should.equal(1); + docs[0].internalAttributes[0].customField.should.equal('customValue'); + docs[0].attributes.length.should.equal(1); + docs[0].attributes[0].name.should.equal('status'); + done(); + }); + }); + }); + it('should store the service information from the headers into the DB', function (done) { + request(optionsCreation, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs[0].service); + should.exist(docs[0].subservice); + docs[0].service.should.equal('testservice'); + docs[0].subservice.should.equal('/testingPath'); + done(); + }); + }); + }); + }); + + describe('When a new device group creation request arrives with an existant (apikey, resource) pair', function () { + it('should return a DUPLICATE_GROUP error', function (done) { + request(optionsCreation, function (error, response, body) { + request(optionsCreation, function (error, response, body) { + response.statusCode.should.equal(409); + body.name.should.equal('DUPLICATE_GROUP'); + done(); + }); + }); + }); + }); + + describe('When a device group removal request arrives', function () { + beforeEach(function (done) { + request(optionsCreation, done); + }); + + it('should remove it from the database', function (done) { + request(optionsDelete, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs); + should.exist(docs.length); + docs.length.should.equal(0); + done(); + }); + }); + }); + + it('should return a 204 OK statusCode', function (done) { + request(optionsDelete, function (error, response, body) { + response.statusCode.should.equal(204); + done(); + }); + }); + }); + + describe('When a device group update request arrives', function () { + beforeEach(function (done) { + request(optionsCreation, done); + }); + + it('should update the values in the database', function (done) { + request(optionsUpdate, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs); + should.exist(docs[0].cbHost); + docs[0].cbHost.should.equal('http://anotherUnexistentHost:1026'); + should.exist(docs[0].staticAttributes); + docs[0].staticAttributes.length.should.equal(1); + done(); + }); + }); + }); + }); + + describe('When a multiple device group creation arrives', function () { + const optionsMultipleCreation = _.clone(optionsCreation); + + beforeEach(function (done) { + optionsMultipleCreation.json = utils.readExampleFile( + './test/unit/examples/groupProvisioningRequests/multipleGroupsCreation.json' + ); + + done(); + }); + + it('should create the values in the database', function (done) { + request(optionsMultipleCreation, function (error, response, body) { + iotAgentDb + .db() + .collection('groups') + .find({}) + .toArray(function (err, docs) { + should.not.exist(err); + should.exist(docs); + docs.length.should.equal(2); + done(); + }); + }); + }); + }); + + describe('When a device group listing request arrives', function () { + beforeEach(function (done) { + const optionsCreation1 = _.clone(optionsCreation); + const optionsCreation2 = _.clone(optionsCreation); + const optionsCreation3 = _.clone(optionsCreation); + + optionsCreation2.json = { services: [] }; + optionsCreation3.json = { services: [] }; + + optionsCreation2.json.services[0] = _.clone(optionsCreation.json.services[0]); + optionsCreation3.json.services[0] = _.clone(optionsCreation.json.services[0]); + + optionsCreation2.json.services[0].apikey = 'qwertyuiop'; + optionsCreation3.json.services[0].apikey = 'lkjhgfds'; + + async.series( + [ + async.apply(request, optionsCreation1), + async.apply(request, optionsCreation2), + async.apply(request, optionsCreation3) + ], + done + ); + }); + + it('should return all the configured device groups from the database', function (done) { + request(optionsList, function (error, response, body) { + body.count.should.equal(3); + done(); + }); + }); + }); + + describe('When a device group listing arrives with a limit', function () { + const optionsConstrained = { + url: 'http://localhost:4041/iot/services', + method: 'GET', + qs: { + limit: 3, + offset: 2 + }, + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/*' + } + }; + + beforeEach(function (done) { + const optionsCreationList = []; + const creationFns = []; + + for (let i = 0; i < 10; i++) { + optionsCreationList[i] = _.clone(optionsCreation); + optionsCreationList[i].json = { services: [] }; + optionsCreationList[i].json.services[0] = _.clone(optionsCreation.json.services[0]); + optionsCreationList[i].json.services[0].apikey = 'qwertyuiop' + i; + creationFns.push(async.apply(request, optionsCreationList[i])); + } + + async.series(creationFns, done); + }); + + it('should return the appropriate count of services', function (done) { + request(optionsConstrained, function (error, response, body) { + body.count.should.equal(10); + done(); + }); + }); + }); + + describe('When a device info request arrives', function () { + beforeEach(function (done) { + async.series([async.apply(request, optionsCreation)], done); + }); + + it('should return all the configured device groups from the database', function (done) { + request(optionsGet, function (error, response, body) { + should.exist(body); + should.exist(body.count); + body.count.should.equal(1); + should.exist(body.services); + should.exist(body.services.length); + body.services.length.should.equal(1); + body.services[0].service.should.equal('testservice'); + done(); + }); + }); + }); + + describe('When a device info request arrives and multiple groups have been created', function () { + beforeEach(function (done) { + const optionsCreationList = []; + const creationFns = []; + + for (let i = 0; i < 10; i++) { + optionsCreationList[i] = _.clone(optionsCreation); + optionsCreationList[i].json = { services: [] }; + optionsCreationList[i].json.services[0] = _.clone(optionsCreation.json.services[0]); + optionsCreationList[i].json.services[0].apikey = 'qwertyuiop' + i; + creationFns.push(async.apply(request, optionsCreationList[i])); + } + + async.series(creationFns, done); + }); + + it('should return all the configured device groups from the database', function (done) { + request(optionsGet, function (error, response, body) { + should.exist(body); + should.exist(body.count); + body.count.should.equal(10); + should.exist(body.services); + should.exist(body.services.length); + body.services.length.should.equal(10); + done(); + }); + }); + }); + + describe('When the device info request with name and type', function () { + beforeEach(function (done) { + async.series([async.apply(request, optionsCreation)], done); + }); + + afterEach(function (done) { + iotAgentLib.clearRegistry(done); + }); + + it('should return the name and type of device', function (done) { + request(optionsGet, function (error, response, body) { + should.exist(body); + should.exist(body.count); + body.count.should.equal(1); + should.exist(body.services); + should.exist(body.services.length); + body.services.length.should.equal(1); + should.exist(body.services[0].entity_type); + body.services[0].entity_type.should.equal('Light'); + done(); + }); + }); + }); +}); diff --git a/test/unit/ngsiv2/general/deviceService-test.js b/test/unit/ngsiv2/general/deviceService-test.js index ce4a4043a..4680e705f 100644 --- a/test/unit/ngsiv2/general/deviceService-test.js +++ b/test/unit/ngsiv2/general/deviceService-test.js @@ -156,6 +156,34 @@ const groupCreation = { 'fiware-servicepath': '/testingPath' } }; +const configGroupCreation = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + entity_type: 'TheLightType', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://192.168.1.1:1026', + commands: [], + lazy: [], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ], + static_attributes: [] + } + ] + }, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } +}; const deviceCreation = { url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', method: 'POST', @@ -182,7 +210,7 @@ describe('NGSI-v2 - Device Service: utils', function () { nock.cleanAll(); async.series([iotAgentLib.clearAll, iotAgentLib.deactivate], done); }); - + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When an existing device tries to be retrieved with retrieveOrCreate()', function () { beforeEach(function (done) { // This mock does not check the payload since the aim of the test is not to verify @@ -212,7 +240,39 @@ describe('NGSI-v2 - Device Service: utils', function () { }); }); }); + describe('When an existing device tries to be retrieved with retrieveOrCreate()', function () { + beforeEach(function (done) { + // This mock does not check the payload since the aim of the test is not to verify + // device provisioning functionality. Appropriate verification is done in tests under + // provisioning folder + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + async.series( + [ + utils.request.bind(utils.request, configGroupCreation), + utils.request.bind(utils.request, deviceCreation) + ], + function (error, results) { + done(); + } + ); + }); + + it('should return the existing device', function (done) { + iotAgentLib.retrieveDevice('Light1', '801230BJKL23Y9090DSFL123HJK09H324HV8732', function (error, device) { + should.not.exist(error); + should.exist(device); + device.id.should.equal('Light1'); + done(); + }); + }); + }); + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When an unexisting device tries to be retrieved for an existing APIKey', function () { beforeEach(function (done) { // This mock does not check the payload since the aim of the test is not to verify @@ -246,6 +306,39 @@ describe('NGSI-v2 - Device Service: utils', function () { }); }); + describe('When an unexisting device tries to be retrieved for an existing APIKey', function () { + beforeEach(function (done) { + // This mock does not check the payload since the aim of the test is not to verify + // device provisioning functionality. Appropriate verification is done in tests under + // provisioning folder + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + async.series([utils.request.bind(utils.request, configGroupCreation)], function (error, results) { + done(); + }); + }); + + it('should register the device and return it', function (done) { + iotAgentLib.retrieveDevice( + 'UNEXISTENT_DEV', + '801230BJKL23Y9090DSFL123HJK09H324HV8732', + function (error, device) { + should.not.exist(error); + should.exist(device); + + device.id.should.equal('UNEXISTENT_DEV'); + should.exist(device.protocol); + device.protocol.should.equal('MQTT_UL'); + done(); + } + ); + }); + }); + describe('When an unexisting device tries to be retrieved for an unexisting APIKey', function () { it('should raise an error', function (done) { iotAgentLib.retrieveDevice( diff --git a/test/unit/ngsiv2/general/iotam-autoregistration-test.js b/test/unit/ngsiv2/general/iotam-autoregistration-test.js index 6c38e5771..1f15fa952 100644 --- a/test/unit/ngsiv2/general/iotam-autoregistration-test.js +++ b/test/unit/ngsiv2/general/iotam-autoregistration-test.js @@ -134,6 +134,43 @@ const optionsCreation = { 'fiware-servicepath': 'theSubService' } }; +const configGroupCreation = { + url: 'http://localhost:4041/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + entity_type: 'SensorMachine', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://unexistentHost:1026', + commands: [ + { + name: 'wheel1', + type: 'Wheel' + } + ], + lazy: [ + { + name: 'luminescence', + type: 'Lumens' + } + ], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ] + } + ] + }, + headers: { + 'fiware-service': 'theservice', + 'fiware-servicepath': 'theSubService' + } +}; const optionsCreationStatic = { url: 'http://localhost:4041/iot/services', method: 'POST', @@ -172,6 +209,44 @@ const optionsCreationStatic = { 'fiware-servicepath': 'theSubService' } }; +const configGroupCreationStatic = { + url: 'http://localhost:4041/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + entity_type: 'SensorMachine', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://unexistentHost:1026', + commands: [ + { + name: 'wheel1', + type: 'Wheel' + } + ], + static_attributes: [ + { + name: 'position', + type: 'location', + values: '123,12' + } + ], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ] + } + ] + }, + headers: { + 'fiware-service': 'theservice', + 'fiware-servicepath': 'theSubService' + } +}; const optionsDelete = { url: 'http://localhost:4041/iot/services', method: 'DELETE', @@ -185,6 +260,19 @@ const optionsDelete = { apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' } }; +const configGroupDelete = { + url: 'http://localhost:4041/iot/groups', + method: 'DELETE', + json: {}, + headers: { + 'fiware-service': 'theservice', + 'fiware-servicepath': 'theSubService' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; let iotamMock; describe('NGSI-v2 - IoT Manager autoregistration', function () { @@ -263,6 +351,7 @@ describe('NGSI-v2 - IoT Manager autoregistration', function () { }); }); + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When a new service is created in the IoT Agent', function () { beforeEach(function (done) { nock.cleanAll(); @@ -298,6 +387,42 @@ describe('NGSI-v2 - IoT Manager autoregistration', function () { }); }); + describe('When a new configGroup is created in the IoT Agent', function () { + beforeEach(function (done) { + nock.cleanAll(); + + iotamMock = nock('http://mockediotam.com:9876') + .post('/protocols', utils.readExampleFile('./test/unit/examples/iotamRequests/registrationEmpty.json')) + .reply(200, utils.readExampleFile('./test/unit/examples/iotamResponses/registrationSuccess.json')); + + iotamMock + .post( + '/protocols', + utils.readExampleFile('./test/unit/examples/iotamRequests/registrationWithGroups.json') + ) + .reply(200, utils.readExampleFile('./test/unit/examples/iotamResponses/registrationSuccess.json')); + + iotAgentLib.activate(iotAgentConfig, function (error) { + done(); + }); + }); + + afterEach(function (done) { + groupRegistryMemory.clear(function () { + iotAgentLib.deactivate(done); + }); + }); + + it('should update the registration in the IoT Manager', function (done) { + request(configGroupCreation, function (error, result, body) { + should.not.exist(error); + iotamMock.done(); + done(); + }); + }); + }); + + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When a service is removed from the IoT Agent', function () { beforeEach(function (done) { nock.cleanAll(); @@ -333,6 +458,42 @@ describe('NGSI-v2 - IoT Manager autoregistration', function () { }); }); + describe('When a configGroup is removed from the IoT Agent', function () { + beforeEach(function (done) { + nock.cleanAll(); + + iotamMock = nock('http://mockediotam.com:9876') + .post( + '/protocols', + utils.readExampleFile('./test/unit/examples/iotamRequests/registrationWithGroups.json') + ) + .reply(200, utils.readExampleFile('./test/unit/examples/iotamResponses/registrationSuccess.json')); + + iotamMock + .post('/protocols', utils.readExampleFile('./test/unit/examples/iotamRequests/registrationEmpty.json')) + .reply(200, utils.readExampleFile('./test/unit/examples/iotamResponses/registrationSuccess.json')); + + groupRegistryMemory.create(groupCreation, function () { + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function (done) { + groupRegistryMemory.clear(function () { + iotAgentLib.deactivate(done); + }); + }); + + it('should update the registration in the IoT Manager', function (done) { + request(configGroupDelete, function (error, result, body) { + should.not.exist(error); + iotamMock.done(); + done(); + }); + }); + }); + + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When a new service with static attributes is created in the IoT Agent', function () { beforeEach(function (done) { nock.cleanAll(); @@ -367,4 +528,38 @@ describe('NGSI-v2 - IoT Manager autoregistration', function () { }); }); }); + describe('When a new configGroup with static attributes is created in the IoT Agent', function () { + beforeEach(function (done) { + nock.cleanAll(); + + iotamMock = nock('http://mockediotam.com:9876') + .post('/protocols', utils.readExampleFile('./test/unit/examples/iotamRequests/registrationEmpty.json')) + .reply(200, utils.readExampleFile('./test/unit/examples/iotamResponses/registrationSuccess.json')); + + iotamMock + .post( + '/protocols', + utils.readExampleFile('./test/unit/examples/iotamRequests/registrationWithStaticGroups.json') + ) + .reply(200, utils.readExampleFile('./test/unit/examples/iotamResponses/registrationSuccess.json')); + + iotAgentLib.activate(iotAgentConfig, function (error) { + done(); + }); + }); + + afterEach(function (done) { + groupRegistryMemory.clear(function () { + iotAgentLib.deactivate(done); + }); + }); + + it('should update the registration in the IoT Manager', function (done) { + request(configGroupCreationStatic, function (error, result, body) { + should.not.exist(error); + iotamMock.done(); + done(); + }); + }); + }); }); diff --git a/test/unit/ngsiv2/provisioning/device-group-api-test.js b/test/unit/ngsiv2/provisioning/device-group-api-test.js index 4a2ae63a0..e2871cff7 100644 --- a/test/unit/ngsiv2/provisioning/device-group-api-test.js +++ b/test/unit/ngsiv2/provisioning/device-group-api-test.js @@ -23,6 +23,8 @@ /* eslint-disable no-unused-vars */ +// FIXME: parallel tests in device-provisioning-configGroup-api_test.js. + const iotAgentLib = require('../../../../lib/fiware-iotagent-lib'); const _ = require('underscore'); const async = require('async'); @@ -250,6 +252,20 @@ const optionsGet = { } }; +// Add new options using the literal groups instead of services +const configGroupTerm = 'groups'; + +const newOptionsCreation = JSON.parse(JSON.stringify(optionsCreation)); +newOptionsCreation.url = newOptionsCreation.url.replace('services', configGroupTerm); +newOptionsCreation.json[configGroupTerm] = newOptionsCreation.json.services; +delete newOptionsCreation.json.services; + +const newOptionsList = JSON.parse(JSON.stringify(optionsList)); +newOptionsList.url = newOptionsList.url.replace('services', configGroupTerm); + +const newOptionsGet = JSON.parse(JSON.stringify(optionsGet)); +newOptionsGet.url = newOptionsGet.url.replace('services', configGroupTerm); + describe('NGSI-v2 - Device Group Configuration API', function () { beforeEach(function (done) { iotAgentLib.activate(iotAgentConfig, function () { @@ -1156,4 +1172,87 @@ describe('NGSI-v2 - Device Group Configuration API', function () { }); }); }); + + describe('When a new device group creation request arrives with the NEW API endpoint ', function () { + it('should return a 200 OK', function (done) { + request(newOptionsCreation, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + done(); + }); + }); + it('should be recovered using the OLD API endpoint', function (done) { + request(newOptionsCreation, function (error, response, body) { + request(optionsList, function (error, response, body) { + body.count.should.equal(1); + body.services[0].apikey.should.equal('801230BJKL23Y9090DSFL123HJK09H324HV8732'); + body.services[0].transport.should.equal('HTTP'); + body.services[0].endpoint.should.equal('http://myendpoint.com'); + + body.count.should.equal(1); + should.exist(body.services[0].attributes); + body.services[0].attributes.length.should.equal(1); + body.services[0].attributes[0].name.should.equal('status'); + + should.exist(body.services[0].lazy); + body.services[0].lazy.length.should.equal(1); + body.services[0].lazy[0].name.should.equal('luminescence'); + + should.exist(body.services[0].commands); + body.services[0].commands.length.should.equal(1); + body.services[0].commands[0].name.should.equal('wheel1'); + + should.exist(body.services[0].static_attributes); + body.services[0].static_attributes.length.should.equal(1); + body.services[0].static_attributes[0].name.should.equal('bootstrapServer'); + + body.count.should.equal(1); + body.services[0].service.should.equal('testservice'); + body.services[0].subservice.should.equal('/testingPath'); + done(); + }); + }); + }); + }); + describe('When a new device group creation request arrives with the NEW OLD endpoint ', function () { + it('should return a 200 OK', function (done) { + request(optionsCreation, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + done(); + }); + }); + it('should be recovered using the NEW API endpoint', function (done) { + request(optionsCreation, function (error, response, body) { + request(newOptionsList, function (error, response, body) { + body.count.should.equal(1); + body[configGroupTerm][0].apikey.should.equal('801230BJKL23Y9090DSFL123HJK09H324HV8732'); + body[configGroupTerm][0].transport.should.equal('HTTP'); + body[configGroupTerm][0].endpoint.should.equal('http://myendpoint.com'); + + body.count.should.equal(1); + should.exist(body[configGroupTerm][0].attributes); + body[configGroupTerm][0].attributes.length.should.equal(1); + body[configGroupTerm][0].attributes[0].name.should.equal('status'); + + should.exist(body[configGroupTerm][0].lazy); + body[configGroupTerm][0].lazy.length.should.equal(1); + body[configGroupTerm][0].lazy[0].name.should.equal('luminescence'); + + should.exist(body[configGroupTerm][0].commands); + body[configGroupTerm][0].commands.length.should.equal(1); + body[configGroupTerm][0].commands[0].name.should.equal('wheel1'); + + should.exist(body[configGroupTerm][0].static_attributes); + body[configGroupTerm][0].static_attributes.length.should.equal(1); + body[configGroupTerm][0].static_attributes[0].name.should.equal('bootstrapServer'); + + body.count.should.equal(1); + body[configGroupTerm][0].service.should.equal('testservice'); + body[configGroupTerm][0].subservice.should.equal('/testingPath'); + done(); + }); + }); + }); + }); }); diff --git a/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js b/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js new file mode 100644 index 000000000..82a13a9b9 --- /dev/null +++ b/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js @@ -0,0 +1,1189 @@ +/* + * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U + * + * This file is part of fiware-iotagent-lib + * + * fiware-iotagent-lib is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the License, + * or (at your option) any later version. + * + * fiware-iotagent-lib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with fiware-iotagent-lib. + * If not, see http://www.gnu.org/licenses/. + * + * For those usages not covered by the GNU Affero General Public License + * please contact with::[contacto@tid.es] + * + * Modified by: Daniel Calvo - ATOS Research & Innovation + */ + +/* eslint-disable no-unused-vars */ + +const iotAgentLib = require('../../../../lib/fiware-iotagent-lib'); +const utils = require('../../../tools/utils'); +const should = require('should'); +const nock = require('nock'); + +const request = utils.request; +const moment = require('moment'); +let contextBrokerMock; +const iotAgentConfig = { + logLevel: 'FATAL', + contextBroker: { + host: '192.168.1.1', + port: '1026', + ngsiVersion: 'v2' + }, + server: { + port: 4041, + host: 'localhost', + baseRoot: '/' + }, + types: {}, + service: 'smartgondor', + subservice: 'gardens', + providerUrl: 'http://smartgondor.com', + explicitAttrs: false +}; + +describe('NGSI-v2 - Device provisioning API: Provision devices', function () { + beforeEach(function (done) { + nock.cleanAll(); + + iotAgentLib.activate(iotAgentConfig, function () { + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post( + '/v2/registrations', + utils.readExampleFile( + './test/unit/ngsiv2/examples/contextAvailabilityRequests/registerProvisionedDevice.json' + ) + ) + .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + + // This mock does not check the payload since the aim of the test is not to verify + // device provisioning functionality. Appropriate verification is done in tests under + // provisioning folder + contextBrokerMock + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post('/v2/entities?options=upsert') + .reply(204); + + iotAgentLib.clearAll(done); + }); + }); + + afterEach(function (done) { + nock.cleanAll(); + iotAgentLib.setProvisioningHandler(); + iotAgentLib.deactivate(done); + }); + + describe('When a device provisioning request with all the required data arrives to the IoT Agent', function () { + beforeEach(function () { + nock.cleanAll(); + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post( + '/v2/registrations', + utils.readExampleFile( + './test/unit/ngsiv2/examples/contextAvailabilityRequests/registerProvisionedDevice.json' + ) + ) + .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + }); + + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionNewDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + it('should add the device to the devices list', function (done) { + request(options, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + results.devices.length.should.equal(1); + done(); + }); + }); + }); + + it('should call the device provisioning handler if present', function (done) { + let handlerCalled = false; + + iotAgentLib.setProvisioningHandler(function (device, callback) { + handlerCalled = true; + callback(null, device); + }); + + request(options, function (error, response, body) { + handlerCalled.should.equal(true); + done(); + }); + }); + + it('should store the device with the provided entity id, name and type', function (done) { + request(options, function (error, response, body) { + response.statusCode.should.equal(201); + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + results.devices[0].id.should.equal('Light1'); + results.devices[0].name.should.equal('TheFirstLight'); + results.devices[0].type.should.equal('TheLightType'); + done(); + }); + }); + }); + it('should store the device with the per device information', function (done) { + request(options, function (error, response, body) { + response.statusCode.should.equal(201); + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.exist(results.devices[0].timezone); + results.devices[0].timezone.should.equal('America/Santiago'); + should.exist(results.devices[0].endpoint); + results.devices[0].endpoint.should.equal('http://fakedEndpoint:1234'); + should.exist(results.devices[0].transport); + results.devices[0].transport.should.equal('MQTT'); + should.exist(results.devices[0].lazy); + results.devices[0].lazy.length.should.equal(1); + results.devices[0].lazy[0].name.should.equal('luminance'); + should.exist(results.devices[0].staticAttributes); + results.devices[0].commands.length.should.equal(1); + results.devices[0].commands[0].name.should.equal('commandAttr'); + should.exist(results.devices[0].staticAttributes); + results.devices[0].staticAttributes.length.should.equal(1); + results.devices[0].staticAttributes[0].name.should.equal('hardcodedAttr'); + should.exist(results.devices[0].active); + results.devices[0].active.length.should.equal(1); + results.devices[0].active[0].name.should.equal('attr_name'); + should.exist(results.devices[0].internalAttributes); + results.devices[0].internalAttributes.length.should.equal(1); + results.devices[0].internalAttributes[0].customField.should.equal('customValue'); + done(); + }); + }); + }); + + it('should store fill the device ID in case only the name is provided', function (done) { + /* jshint camelcase:false */ + request(options, function (error, response, body) { + response.statusCode.should.equal(201); + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + results.devices[0].lazy[0].object_id.should.equal('luminance'); + results.devices[0].commands[0].object_id.should.equal('commandAttr'); + results.devices[0].active[0].object_id.should.equal('attr_name'); + done(); + }); + }); + }); + + it('should store service and subservice info from the headers along with the device data', function (done) { + request(options, function (error, response, body) { + response.statusCode.should.equal(201); + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.exist(results.devices[0].service); + results.devices[0].service.should.equal('smartgondor'); + should.exist(results.devices[0].subservice); + results.devices[0].subservice.should.equal('/gardens'); + done(); + }); + }); + }); + + it('should not create the initial entity in the Context Broker', function (done) { + request(options, function (error, response, body) { + response.statusCode.should.equal(201); + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + contextBrokerMock.done(); + done(); + }); + }); + }); + }); + describe('When a device provisioning request with a TimeInstant attribute arrives to the IoTA', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionTimeInstant.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentConfig.timestamp = true; + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function () { + iotAgentConfig.timestamp = false; + }); + + beforeEach(function (done) { + nock.cleanAll(); + done(); + }); + + it('should send the appropriate requests to the Context Broker', function (done) { + request(options, function (error, response, body) { + contextBrokerMock.done(); + done(); + }); + }); + }); + + describe('When a device provisioning request with a timestamp provision attribute arrives to the IoTA', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionTimeInstant2.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentConfig.timestamp = false; + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function () { + iotAgentConfig.timestamp = false; + }); + + beforeEach(function (done) { + nock.cleanAll(); + done(); + }); + + it('should send the appropriate requests to the Context Broker', function (done) { + request(options, function (error, response, body) { + contextBrokerMock.done(); + done(); + }); + }); + }); + + describe('When a device provisioning request with explicitAttrs provision attribute arrives to the IoTA', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionExplicitAttrs.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentConfig.explicitAttrs = false; + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function () { + iotAgentConfig.explicitAttrs = false; + }); + + beforeEach(function (done) { + nock.cleanAll(); + done(); + }); + + it('should send the appropriate requests to the Context Broker', function (done) { + request(options, function (error, response, body) { + contextBrokerMock.done(); + done(); + }); + }); + it('should store the device with explicitAttrs:true device information', function (done) { + request(options, function (error, response, body) { + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.exist(results.devices[0].explicitAttrs); + results.devices[0].explicitAttrs.should.equal(true); + done(); + }); + }); + }); + }); + + describe( + 'When a device provisioning request without explicitAttrs provision attribute arrives to the IoTA' + + ' and explicitAttrs is not configured at group level', + function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentConfig.explicitAttrs = false; + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function () { + iotAgentConfig.explicitAttrs = false; + }); + + beforeEach(function (done) { + nock.cleanAll(); + done(); + }); + + it('should send the appropriate requests to the Context Broker', function (done) { + request(options, function (error, response, body) { + contextBrokerMock.done(); + done(); + }); + }); + it('should store the device without explicitAttrs', function (done) { + request(options, function (error, response, body) { + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.not.exist(results.devices[0].explicitAttrs); + done(); + }); + }); + }); + } + ); + + describe( + 'When a device provisioning request without explicitAttrs provision attribute arrives to the IoTA' + + ' and explicitAttrs is configured at group level', + function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const groupCreation = { + url: 'http://localhost:4041/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '/Thing', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + /*jshint camelcase: false */ + entity_type: 'MicroLights', + cbroker: 'http://192.168.1.1:1026', + explicitAttrs: true + } + ] + }, + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + 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', + utils.readExampleFile( + './test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice.json' + ) + ) + .reply(204); + + done(); + }); + + it('should store the device without explicitAttrs value', function (done) { + request(groupCreation, function (error, response, body) { + request(options, function (error, response, body) { + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.not.exist(results.devices[0].explicitAttrs); + done(); + }); + }); + }); + }); + } + ); + + describe( + 'When a device provisioning request without static attributes arrives to the IoTA' + + ' and static attribute is configured at group level', + function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const groupCreation = { + url: 'http://localhost:4041/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '/Thing', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + /*jshint camelcase: false */ + entity_type: 'MicroLights', + cbroker: 'http://192.168.1.1:1026', + explicitAttrs: true, + static_attributes: [ + { + name: 'bootstrapServer', + type: 'Address', + value: '127.0.0.1' + } + ] + } + ] + }, + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + 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', + utils.readExampleFile( + './test/unit/ngsiv2/examples/' + + 'contextRequests/createStaticAttributesProvisionDevice.json' + ) + ) + .reply(204); + + done(); + }); + + it('should not store the device with static attributes provided in configuration', function (done) { + request(groupCreation, function (error, response, body) { + request(options, function (error, response, body) { + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.exist(results.devices[0].staticAttributes); + results.devices[0].staticAttributes.length.should.equal(0); + done(); + }); + }); + }); + }); + } + ); + describe( + 'When a device provisioning request with static attributes arrives to the IoTA' + + ' and static attribute is not configured at group level', + function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionStaticAttrsDevice.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const groupCreation = { + url: 'http://localhost:4041/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '/Thing', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + /*jshint camelcase: false */ + entity_type: 'MicroLights', + cbroker: 'http://192.168.1.1:1026', + explicitAttrs: true + } + ] + }, + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + 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', + utils.readExampleFile( + './test/unit/ngsiv2/examples/' + + 'contextRequests/createStaticAttributesProvisionDevice.json' + ) + ) + .reply(204); + + done(); + }); + + it('should store the device with static attributes provided in device', function (done) { + request(groupCreation, function (error, response, body) { + request(options, function (error, response, body) { + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.exist(results.devices[0].staticAttributes); + results.devices[0].staticAttributes[0].name.should.equal('bootstrapServer'); + done(); + }); + }); + }); + }); + } + ); + + describe( + 'When a device provisioning request with static attributes arrives to the IoTA' + + ' and static attribute is also configured at group level', + function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionStaticAttrsDevice2.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const groupCreation = { + url: 'http://localhost:4041/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '/Thing', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + /*jshint camelcase: false */ + entity_type: 'MicroLights', + cbroker: 'http://192.168.1.1:1026', + explicitAttrs: true, + static_attributes: [ + { + name: 'bootstrapServer', + type: 'Address', + value: '127.0.0.1' + } + ] + } + ] + }, + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + 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', + utils.readExampleFile( + './test/unit/ngsiv2/examples/' + + 'contextRequests/createStaticAttributesProvisionDevice2.json' + ) + ) + .reply(204); + + done(); + }); + + it('should store the device with static attributes provided in configuration as well as device', function (done) { + request(groupCreation, function (error, response, body) { + request(options, function (error, response, body) { + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.exist(results.devices[0].staticAttributes); + results.devices[0].staticAttributes.length.should.equal(1); + done(); + }); + }); + }); + }); + } + ); + + describe( + 'When a device provisioning request with static attributes arrives to the IoTA' + + ' and same static attribute is also configured at group level', + function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionStaticAttrsDevice3.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const groupCreation = { + url: 'http://localhost:4041/iot/groups', + method: 'POST', + json: { + groups: [ + { + resource: '/Thing', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + /*jshint camelcase: false */ + entity_type: 'MicroLights', + cbroker: 'http://192.168.1.1:1026', + explicitAttrs: true, + static_attributes: [ + { + name: 'bootstrapServer', + type: 'Address', + value: '127.0.0.1' + } + ] + } + ] + }, + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + 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', + utils.readExampleFile( + './test/unit/ngsiv2/examples/' + + 'contextRequests/createStaticAttributesProvisionDevice3.json' + ) + ) + .reply(204); + + done(); + }); + + it('should store the device with static attributes provided in device', function (done) { + request(groupCreation, function (error, response, body) { + request(options, function (error, response, body) { + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + should.exist(results.devices[0].staticAttributes); + results.devices[0].staticAttributes[0].value.should.equal('127.0.0.2'); + done(); + }); + }); + }); + }); + } + ); + + describe('When a device provisioning request with a autoprovision attribute arrives to the IoTA', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionAutoprovision.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function () {}); + + beforeEach(function (done) { + nock.cleanAll(); + done(); + }); + + it('should send the appropriate requests to the Context Broker', function (done) { + request(options, function (error, response, body) { + done(); + }); + }); + }); + + describe('When a device provisioning request arrives to the IoTAand timestamp is enabled in configuration', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentConfig.timestamp = true; + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function () { + iotAgentConfig.timestamp = false; + }); + + beforeEach(function (done) { + nock.cleanAll(); + + done(); + }); + + it('should send the any request to the Context Broker', function (done) { + request(options, function (error, response, body) { + done(); + }); + }); + }); + + describe('When a device provisioning request with the minimum required data arrives to the IoT Agent', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + nock.cleanAll(); + done(); + }); + + it('should send the any request to the Context Broker', function (done) { + request(options, function (error, response, body) { + done(); + }); + }); + + it('should add the device to the devices list', function (done) { + request(options, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + results.devices.length.should.equal(1); + done(); + }); + }); + }); + + it('should store the device with the provided entity id, name and type', function (done) { + request(options, function (error, response, body) { + response.statusCode.should.equal(201); + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + results.devices[0].id.should.equal('MicroLight1'); + results.devices[0].name.should.equal('FirstMicroLight'); + results.devices[0].type.should.equal('MicroLights'); + done(); + }); + }); + }); + }); + + describe('When a device provisioning request with geo:point attributes arrives', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionGeopointDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + nock.cleanAll(); + done(); + }); + + it('should send any initial values to the Context Broker', function (done) { + request(options, function (error, response, body) { + done(); + }); + }); + }); + + describe('When a device provisioning request with DateTime attributes arrives', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionDatetimeDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + nock.cleanAll(); + done(); + }); + + it('should send any initial values to the Context Broker', function (done) { + request(options, function (error, response, body) { + done(); + }); + }); + }); + + describe('When two devices with the same ID but different services arrive to the agent', function () { + const options1 = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const options2 = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json'), + headers: { + 'fiware-service': 'smartMordor', + 'fiware-servicepath': '/electricity' + } + }; + + beforeEach(function (done) { + nock.cleanAll(); + contextBrokerMock = nock('http://192.168.1.1:1026') + .post( + '/v2/entities?options=upsert', + utils.readExampleFile( + './test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice.json' + ) + ) + .reply(204); + + contextBrokerMock + .post( + '/v2/entities?options=upsert', + utils.readExampleFile( + './test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice.json' + ) + ) + .reply(204); + + done(); + }); + + it('should accept both creations', function (done) { + request(options1, function (error, response, body) { + response.statusCode.should.equal(201); + + request(options2, function (error, response, body) { + response.statusCode.should.equal(201); + done(); + }); + }); + }); + + it('should show the new device in each list', function (done) { + request(options1, function (error, response, body) { + request(options2, function (error, response, body) { + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + results.devices.length.should.equal(1); + results.devices[0].id.should.equal('MicroLight1'); + + iotAgentLib.listDevices('smartMordor', '/electricity', function (error, results) { + results.devices.length.should.equal(1); + results.devices[0].id.should.equal('MicroLight1'); + done(); + }); + }); + }); + }); + }); + }); + + describe('When there is a connection error with a String code connecting the CB', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post('/v2/registrations') + .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + done(); + }); + + it('should return a valid return code', function (done) { + request(options, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + + done(); + }); + }); + }); + + describe('When there is a connection error with a Number code connecting the CB', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post('/v2/registrations') + .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + + done(); + }); + + it('should return a valid return code (three character number)', function (done) { + request(options, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + + done(); + }); + }); + }); + + describe('When a device provisioning request with missing data arrives to the IoT Agent', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + }, + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionDeviceMissingParameters.json' + ) + }; + + it('should raise a MISSING_ATTRIBUTES error, indicating the missing attributes', function (done) { + request(options, function (error, response, body) { + should.exist(body); + response.statusCode.should.equal(400); + body.name.should.equal('MISSING_ATTRIBUTES'); + body.message.should.match(/.*device_id.*/); + done(); + }); + }); + }); + describe('When two device provisioning requests with the same service and Device ID arrive', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionNewDevice.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function (done) { + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post('/v2/registrations') + .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + + // This mock does not check the payload since the aim of the test is not to verify + // device provisioning functionality. Appropriate verification is done in tests under + // provisioning folder + contextBrokerMock + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post('/v2/entities?options=upsert') + .reply(204); + + done(); + }); + + it('should raise a DUPLICATE_ID error, indicating the ID was already in use', function (done) { + request(options, function (error, response, body) { + request(options, function (error, response, body) { + should.exist(body); + response.statusCode.should.equal(409); + body.name.should.equal('DUPLICATE_DEVICE_ID'); + done(); + }); + }); + }); + }); + describe('When a device provisioning request is malformed', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionNewDeviceMalformed1.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + it('should raise a WRONG_SYNTAX exception', function (done) { + request(options, function (error, response, body) { + request(options, function (error, response, body) { + should.exist(body); + response.statusCode.should.equal(400); + body.name.should.equal('WRONG_SYNTAX'); + done(); + }); + }); + }); + }); + describe('When an agent is activated with a different base root', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/newBaseRoot/iot/devices', + method: 'POST', + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + }, + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionNewDevice.json') + }; + + beforeEach(function (done) { + iotAgentLib.deactivate(function () { + iotAgentConfig.server.baseRoot = '/newBaseRoot'; + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function () { + iotAgentConfig.server.baseRoot = '/'; + }); + + it('should listen to requests in the new root', function (done) { + request(options, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + + iotAgentLib.listDevices('smartgondor', '/gardens', function (error, results) { + results.devices.length.should.equal(1); + done(); + }); + }); + }); + }); + describe('When a device provisioning request without the mandatory headers arrives to the Agent', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + headers: {}, + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionDeviceMissingParameters.json' + ) + }; + + it('should raise a MISSING_HEADERS error, indicating the missing attributes', function (done) { + request(options, function (error, response, body) { + should.exist(body); + response.statusCode.should.equal(400); + body.name.should.equal('MISSING_HEADERS'); + done(); + }); + }); + }); + describe('When a device delete request arrives to the Agent for a not existing device', function () { + const options = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices/Light84', + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + }, + method: 'DELETE' + }; + + it('should return a 404 error', function (done) { + request(options, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(404); + done(); + }); + }); + }); +}); From 936d78cc08e107b11275866519e221f88f134b6f Mon Sep 17 00:00:00 2001 From: mapedraza Date: Mon, 26 Aug 2024 10:05:38 +0200 Subject: [PATCH 2/7] Fix code --- .../deviceGroupAdministrationServer.js | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/lib/services/northBound/deviceGroupAdministrationServer.js b/lib/services/northBound/deviceGroupAdministrationServer.js index 7cd9f7c73..5b170a6c5 100644 --- a/lib/services/northBound/deviceGroupAdministrationServer.js +++ b/lib/services/northBound/deviceGroupAdministrationServer.js @@ -124,7 +124,7 @@ function applyConfigurationMiddlewares(newConfiguration, callback) { * @param {Function} next Invokes the next middleware in the chain. */ function handleCreateDeviceGroup(req, res, next) { - req.body = checkAndModifyGroupRecv(req._parsedUrl.pathname, req.body); + req.body = checkAndModifyGroupRecv(req._parsedUrl.pathname, req.body); // #FIXME1649: This line should be removed when /iot/services support is removed for (let i = 0; i < req.body.services.length; i++) { req.body.services[i] = applyMap(apiToInternal, req.body.services[i]); @@ -169,7 +169,7 @@ function handleListDeviceGroups(req, res, next) { } else { translatedGroup = applyMap(internalToApi, group); } - res.status(200).send(checkAndModifyGroupResp(req._parsedUrl.pathname, translatedGroup)); + res.status(200).send(checkAndModifyGroupResp(req._parsedUrl.pathname, translatedGroup)); // #FIXME1649: Response should not use checkAndModifyGroupResp function when /iot/services support is removed } }; @@ -324,32 +324,21 @@ function clear(callback) { callback(); } -function renameObjetKeys(keysMap, obj) { - return Object.keys(obj).reduce((acc, key) => { - // Si la clave existe en el mapa, usar la clave renombrada - const renamedKey = keysMap[key] || key; - - // Agregar la clave renombrada al acumulador - acc[renamedKey] = obj[key]; - - return acc; - }, {}); -} - +// #FIXME1649: This function should be removed when /iot/services support is removed function checkAndModifyGroupResp(route, payload) { - if (route === '/iot/services') { - return payload; - } else { - return renameObjetKeys({ services: 'groups' }, payload); + if (route === '/iot/groups') { + payload['groups'] = payload['services']; + delete payload['services']; } + return payload; } - +// #FIXME1649: This function should be removed when /iot/services support is removed function checkAndModifyGroupRecv(route, payload) { - if (route === '/iot/services') { - return payload; - } else { - return renameObjetKeys({ groups: 'services' }, payload); + if (route === '/iot/groups') { + payload['services'] = payload['groups']; + delete payload['groups']; } + return payload; } exports.loadContextRoutes = loadContextRoutes; From 517cdb775fdcc6801045322e4e181406a9a12d8a Mon Sep 17 00:00:00 2001 From: mapedraza Date: Mon, 26 Aug 2024 10:09:41 +0200 Subject: [PATCH 3/7] Add FIXME message to routes --- lib/services/northBound/deviceGroupAdministrationServer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/services/northBound/deviceGroupAdministrationServer.js b/lib/services/northBound/deviceGroupAdministrationServer.js index 5b170a6c5..2ad2f8a21 100644 --- a/lib/services/northBound/deviceGroupAdministrationServer.js +++ b/lib/services/northBound/deviceGroupAdministrationServer.js @@ -270,6 +270,7 @@ function handleDeleteDeviceGroups(req, res, next) { * * @param {Object} router Express request router object. */ +// #FIXME1649: Routes using /iot/services path should be removed when support for it is removed function loadContextRoutes(router) { router.post( '/iot/services', From b94b414ea54f68b04a8aa1fb78cfaa430df2bbac Mon Sep 17 00:00:00 2001 From: mapedraza Date: Mon, 26 Aug 2024 10:29:39 +0200 Subject: [PATCH 4/7] Fix DOC --- doc/api.md | 71 ++++++++++++++++++++------------------- doc/deprecated.md | 2 ++ doc/getting-started.md | 38 ++++++++++----------- test/functional/README.md | 39 +++++++++++---------- 4 files changed, 80 insertions(+), 70 deletions(-) diff --git a/doc/api.md b/doc/api.md index 4a2860526..ee74b7edf 100644 --- a/doc/api.md +++ b/doc/api.md @@ -50,10 +50,10 @@ - [Config group API](#config-group-api) - [Config group datamodel](#config-group-datamodel) - [Config group operations](#config-group-operations) - - [Retrieve config groups `GET /iot/services`](#retrieve-config-groups-get-iotservices) - - [Create config group `POST /iot/services`](#create-config-group-post-iotservices) - - [Modify config group `PUT /iot/services`](#modify-config-group-put-iotservices) - - [Remove config group `DELETE /iot/services`](#remove-config-group-delete-iotservices) + - [Retrieve config groups `GET /iot/groups`](#retrieve-config-groups-get-iotgroups) + - [Create config group `POST /iot/groups`](#create-config-group-post-iotgroups) + - [Modify config group `PUT /iot/groups`](#modify-config-group-put-iotgroups) + - [Remove config group `DELETE /iot/groups`](#remove-config-group-delete-iotgroups) - [Device API](#device-api) - [Device datamodel](#device-datamodel) - [Device operations](#device-operations) @@ -136,9 +136,9 @@ For every config group, the pair (resource, apikey) _must_ be unique (as it is u which device). Those operations of the API targeting specific resources will need the use of the `resource` and `apikey` parameters to select the appropriate instance. -Config groups can be created with preconfigured sets of attributes, service and subservice information, security information and other -parameters. The specific parameters that can be configured for a given config group are described in the -[Config group datamodel](#config-group-datamodel) section. +Config groups can be created with preconfigured sets of attributes, service and subservice information, security +information and other parameters. The specific parameters that can be configured for a given config group are described +in the [Config group datamodel](#config-group-datamodel) section. ### Devices @@ -218,7 +218,7 @@ device preprovisioning). Device measures can have four different behaviors: an attribute in the device's entity, for which the IoT Agent will be regitered as Context Provider. The IoT Agent will return an immediate response to the Context Broker, and will be held responsible of contacting the device to perform the command itself using the device specific protocol. Special `status` and `info` attributes should be - update. For each command, its `name` and `type` must be provided. For further information, please refer to + update. For each command, its `name` and `type` must be provided. For further information, please refer to [Command execution](#command-execution) section. All of them have the same syntax, a list of objects with the following attributes: @@ -565,17 +565,18 @@ expression. In all cases the following data is available to all expressions: - `id`: device ID - `entity_name`: NGSI entity Name (principal) - `type`: NGSI entity type (principal) -- `service`: device service (`Fiware-Service`) +- `service`: device service (`Fiware-Service`) - `subservice`: device subservice (`Fiware-ServicePath`) - `staticAttributes`: static attributes defined in the device or config group Additionally, for attribute expressions (`expression`, `entity_name`), `entityNameExp` and metadata expressions (`expression`) the following is available in the **context** used to evalute: -- measures, as `` -- metadata (both for attribute measurement in the case of NGSI-v2 measurements and static attribute) are available in the **context** under the following convention: -`metadata..` or `metadata..` in a similar way of defined -for [Context Broker](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md#metadata-support) +- measures, as `` +- metadata (both for attribute measurement in the case of NGSI-v2 measurements and static attribute) are available in + the **context** under the following convention: `metadata..` or + `metadata..` in a similar way of defined for + [Context Broker](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md#metadata-support) ### Examples of JEXL expressions @@ -736,7 +737,8 @@ e.g.: } ``` -Note that there is no order into metadata structure and there is no warranty about which metadata attribute expression will be evaluated first. +Note that there is no order into metadata structure and there is no warranty about which metadata attribute expression +will be evaluated first. ## Measurement transformation @@ -1070,21 +1072,21 @@ As part of the device to entity mapping process, the IoT Agent creates and updat attribute called `TimeInstant`. This timestamp is represented as two different properties of the mapped entity: - An attribute `TimeInstant` is added to updated entities in the case of NGSI-v2, which captures as an ISO8601 - timestamp when the associated measurement was observed. With NGSI-LD, the Standard - `observedAt` property is used instead + timestamp when the associated measurement was observed. With NGSI-LD, the Standard `observedAt` property is used + instead -- With NGSI-v2, an attribute metadata named `TimeInstant` per active or lazy attribute mapped, which captures as an ISO8601 - timestamp when the associated measurement (represented as attribute value) was observed. With NGSI-LD, the Standard - `observedAt` property-of-a-property is used instead. +- With NGSI-v2, an attribute metadata named `TimeInstant` per active or lazy attribute mapped, which captures as an + ISO8601 timestamp when the associated measurement (represented as attribute value) was observed. With NGSI-LD, the + Standard `observedAt` property-of-a-property is used instead. If timestamp is not explicily defined when sending the measures through the IoT Agent (for further details on how to attach timestamp information to the measures, please refer to the particular IoT Agent implementation documentation), the arrival time on the server when receiving the measurement will be used to generate a `TimeInstant` for both the entity attribute and the attribute metadata. -This functionality can be turned on and off globaly through the use of the `timestamp` configuration flag or `IOTA_TIMESTAMP` -variable as well as `timestamp` flag in device or group provision (in this case, the device or group level flag takes -precedence over the global one). The default value is `true`. +This functionality can be turned on and off globaly through the use of the `timestamp` configuration flag or +`IOTA_TIMESTAMP` variable as well as `timestamp` flag in device or group provision (in this case, the device or group +level flag takes precedence over the global one). The default value is `true`. The `timestamp` configuration value used, according to the priority: @@ -1104,6 +1106,7 @@ the IoTA behaviour is described in the following table: | false | No | TimeInstant and metadata updated with server timestamp | | Not defined | Yes | TimeInstant and metadata updated with measure value | | Not defined | No | TimeInstant and metadata updated with server timestamp | + Some additional considerations to take into account: - If there is an attribute which maps a measure to `TimeInstant` attribute (after @@ -1724,7 +1727,7 @@ Config group is represented by a JSON object with the following fields: The following actions are available under the config group endpoint: -#### Retrieve config groups `GET /iot/services` +#### Retrieve config groups `GET /iot/groups` List all the config groups for the given `fiware-service` and `fiware-servicepath`. The config groups that match the `fiware-servicepath` are returned in any other case. @@ -1748,14 +1751,14 @@ Successful operations return `Content-Type` header with `application/json` value _**Response payload**_ -A JSON object with a services field that contains an array of services that match the request. See the -[config group datamodel](#service-group-datamodel) for more information. +A JSON object with a `groups` field that contains an array of groups that match the request. See the +[config group datamodel](#config-group-datamodel) for more information. Example: ```json { - "services": [ + "groups": [ { "resource": "/deviceTest", "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732", @@ -1792,7 +1795,7 @@ Example: } ``` -#### Create config group `POST /iot/services` +#### Create config group `POST /iot/groups` Creates a set of config groups for the given service and service path. The service and subservice information will taken from the headers, overwritting any preexisting values. @@ -1806,14 +1809,14 @@ _**Request headers**_ _**Request payload**_ -A JSON object with a `services` field. The value is an array of config groups objects to create. See the -[config group datamodel](#service-group-datamodel) for more information. +A JSON object with a `groups` field. The value is an array of config groups objects to create. See the +[config group datamodel](#config-group-datamodel) for more information. Example: ```json { - "services": [ + "groups": [ { "resource": "/deviceTest", "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732", @@ -1847,7 +1850,7 @@ _**Response headers**_ Successful operations return `Content-Type` header with `application/json` value. -#### Modify config group `PUT /iot/services` +#### Modify config group `PUT /iot/groups` Modifies the information of a config group, identified by the `resource` and `apikey` query parameters. Takes a service group body as the payload. The body does not have to be complete: for incomplete bodies, just the attributes included in @@ -1869,8 +1872,8 @@ _**Request headers**_ _**Request payload**_ -A JSON object with the config group information to be modified. See the -[config group datamodel](#service-group-datamodel) for more information. +A JSON object with the config group information to be modified. See the [config group datamodel](config-group-datamodel) +for more information. Example: @@ -1887,7 +1890,7 @@ _**Response code**_ - 400 MISSING_HEADERS if any of the mandatory headers is not present. - 500 SERVER ERROR if there was any error not contemplated above.: -#### Remove config group `DELETE /iot/services` +#### Remove config group `DELETE /iot/groups` Removes a config group, identified by the `resource` and `apikey` query parameters. diff --git a/doc/deprecated.md b/doc/deprecated.md index 754bf9914..4fe3960de 100644 --- a/doc/deprecated.md +++ b/doc/deprecated.md @@ -26,6 +26,8 @@ A list of deprecated features and the version in which they were deprecated foll - Bidirectinal pluging (finally removed in 3.4.0) - appendMode configuration (`IOTA_APPEND_MODE` env var) (finally removed in 3.4.0) - `config.stats` section, and push-mode statistics. +- Services API routes (`/iot/services`) in favor of the `/iot/groups`. Both are still supported, but the former is + deprecated. The use of Node.js v14 is highly recommended. diff --git a/doc/getting-started.md b/doc/getting-started.md index 689ea189d..8973c5700 100644 --- a/doc/getting-started.md +++ b/doc/getting-started.md @@ -10,8 +10,8 @@ ## Introduction -In this guide we will be using the IoT Agent JSON (which is the reference IoTAgent using the IoTAgent Library) as an example to demonstrate how to provision config groups, devices -and how to receive measures from devices. +In this guide we will be using the IoT Agent JSON (which is the reference IoTAgent using the IoTAgent Library) as an +example to demonstrate how to provision config groups, devices and how to receive measures from devices. Be aware that every IoT Agent which uses the library is different, but the concepts for provisioning IoT devices remain the same regardless of protocol. @@ -52,8 +52,9 @@ config = { ``` In this case the context broker hostname is `orion` and is listening on port `1026`, the IoT Agent can be provisioned by -sending requests to port `4041` which is also the port used to receive NGSI requests. The IoT Agent is using the `iotagent` -database from a MongoDB instance at `localhost:27017` to store needed information (provisioned groups and devices, etc.). +sending requests to port `4041` which is also the port used to receive NGSI requests. The IoT Agent is using the +`iotagent` database from a MongoDB instance at `localhost:27017` to store needed information (provisioned groups and +devices, etc.). The remaining settings help to define the NGSI interactions - the IoT Agent will be using the `fiware-service=openiot` and `fiware-service-path=/`. The default `type`for each created entity is `Thing`, although this can be overridden as @@ -73,12 +74,12 @@ config group is shown below: ```bash curl -iX POST \ - 'http://localhost:4041/iot/services' \ + 'http://localhost:4041/iot/groups' \ -H 'Content-Type: application/json' \ -H 'fiware-service: openiot' \ -H 'fiware-servicepath: /' \ -d '{ - "services": [ + "groups": [ { "apikey": "4jggokgpepnvsb2uv4s40d59ov", "entity_type": "Device", @@ -95,8 +96,8 @@ In this case an `apiKey` for identifying devices has been created and all intera present this `apiKey` will be created as entities of `type=Device` rather than using the configuration default of `type=Thing`. -Additionally, the group has defined an attribute mapping for a measurement `t` to be mapped to `temperature` attribute when -receiving data from devices. +Additionally, the group has defined an attribute mapping for a measurement `t` to be mapped to `temperature` attribute +when receiving data from devices. The config group would usual hold additional attribute mappings, commands and common static attributes as well. @@ -132,12 +133,12 @@ curl -iX POST \ The device `motion001` has been provisioned to persist data to the Context Broker as an entity of `type=Motion` (instead of the default `type=Thing`). The destination entity is identified by the `entity_name` field, which is set to -`urn:ngsi-ld:Motion:001`. The device has a single attribute mapping for a measurement `c` to be mapped to `count` attribute, -additionally to one defined in the group mapping (`temperature`). The device also has a static attribute `refStore` -which is a relationship to the entity `urn:ngsi-ld:Store:001`. +`urn:ngsi-ld:Motion:001`. The device has a single attribute mapping for a measurement `c` to be mapped to `count` +attribute, additionally to one defined in the group mapping (`temperature`). The device also has a static attribute +`refStore` which is a relationship to the entity `urn:ngsi-ld:Store:001`. -This information is combined with the common config group information whenever a measurement is received at the IoT Agent -and used to create or update the relevant entity in the Context Broker. +This information is combined with the common config group information whenever a measurement is received at the IoT +Agent and used to create or update the relevant entity in the Context Broker. ## Receiving measures from devices @@ -163,8 +164,8 @@ The IoT Agent South port is listening to the path defined in the config group, a Because the `device_id` is also recognized, the provisioned device configuration is used and its settings are combined with the config group. -Mapping has been found to use the `c` measurement as `count` and the `t` measurement as `temperature` attributes values. The following -context entity is created in the context broker: +Mapping has been found to use the `c` measurement as `count` and the `t` measurement as `temperature` attributes values. +The following context entity is created in the context broker: ```json { @@ -179,10 +180,9 @@ context entity is created in the context broker: ### Receiving a measure from an anonymous Device When receiving a measure, it is not necessary to have the device provisioned. In this case, the IoT Agent will use the -config group configuration to create the device and the entity. This process is called "autoprovision" and it is enabled -by default in provisioned groups (for further information, review the [Autoprovision](api.md#autoprovision-configuration-autoprovision) -section in the API documentation). - +config group configuration to create the device and the entity. This process is called "autoprovision" and it is enabled +by default in provisioned groups (for further information, review the +[Autoprovision](api.md#autoprovision-configuration-autoprovision) section in the API documentation). Take as an example the following request from an anonymous device: diff --git a/test/functional/README.md b/test/functional/README.md index 6020a6952..2b29f3d83 100644 --- a/test/functional/README.md +++ b/test/functional/README.md @@ -21,8 +21,10 @@ pattern than the one supported by the test runner). Each test case is defined as a JSON object in the `testCases.js` file. This file is loaded by the test suite and the test cases are automatically generated. Each test case is defined as an object with the following elements: -- `describeName`: The name of the `DESCRIBE` test case. This will be used to generate the test case name in the mocha. Note this name is prefixed by a pure number (e.g `0010 - Simple group without attributes`) which specifies the group to which the test belong (usually meaning a feature) or by a number preced by the `#` symbol to refer to an issue number (e.g. `#1234 - Bug foobar`). - test suite. +- `describeName`: The name of the `DESCRIBE` test case. This will be used to generate the test case name in the mocha. + Note this name is prefixed by a pure number (e.g `0010 - Simple group without attributes`) which specifies the group + to which the test belong (usually meaning a feature) or by a number preced by the `#` symbol to refer to an issue + number (e.g. `#1234 - Bug foobar`). test suite. - `provision`: The JSON object that will be sent to the IoTA JSON provisioning API. This will be used to create the group. It contains the following elements: - `url`: The URL of the provisioning API (group) @@ -30,17 +32,19 @@ test cases are automatically generated. Each test case is defined as an object w - `json`: The JSON object that defines the group - `headers`: The headers to send to the provisioning API. This should contain the `fiware-service` and `fiware-servicepath` headers. - - `skip`: optional. Allow to skip test cases (at `describe` level). It allows diferent values: `false` (default, meaning that the test is not skipped in any circustance), - `true` (meaning the test is always skipped), `"lib"` (meaning the test has to be skipped when running it in IoTA Node lib repo) and - `"json"` (meaning the test has to be skipped when running it in IOTA JSON repo). The latter alternatives are useful to skip test cases that are not supported by - the lib (I.E: all tests related to the transport) or by the IOTA. Combinations (e.g `"lib,json"`) and negation (e.g. `"!lib"`) are also supported. + - `skip`: optional. Allow to skip test cases (at `describe` level). It allows diferent values: `false` (default, + meaning that the test is not skipped in any circustance), `true` (meaning the test is always skipped), `"lib"` + (meaning the test has to be skipped when running it in IoTA Node lib repo) and `"json"` (meaning the test has to + be skipped when running it in IOTA JSON repo). The latter alternatives are useful to skip test cases that are + not supported by the lib (I.E: all tests related to the transport) or by the IOTA. Combinations (e.g + `"lib,json"`) and negation (e.g. `"!lib"`) are also supported. - `should`: The array of test cases to execute. Each test case is defined as an object with the following elements: - `transport`: The transport to use to send the measure. This can be `HTTP` or `MQTT`. It uses `HTTP` by default or if the `transport` element is not defined. See the "Advanced features" section for more information. - `shouldName`: The name of the `IT` test case. This will be used to generate the test case name in the mocha test suite. - - `type`: The type of the test case. This can be `single`, `multimeasure` or `multientity`. See the "Advanced features" section - for more information. + - `type`: The type of the test case. This can be `single`, `multimeasure` or `multientity`. See the "Advanced + features" section for more information. - `measure`: The JSON object that will be sent to the IoTA JSON measure API. This will be used to send the measure. It contains the following elements: - `url`: The URL of the measure API (group) @@ -64,10 +68,10 @@ test cases are automatically generated. Each test case is defined as an object w { describeName: 'Basic group provision with attributes', provision: { - url: 'http://localhost:' + config.iota.server.port + '/iot/services', + url: 'http://localhost:' + config.iota.server.port + '/iot/groups', method: 'POST', json: { - services: [ + groups: [ { resource: '/iot/json', apikey: '123456', @@ -199,11 +203,11 @@ as a batch operation (see the following example). #### Multimeasures -It is also supported to test cases in which is sent more than one measure. To do so, you need to set add to the test case -the parameter `should.type` to the value `'multimeasure'`. +It is also supported to test cases in which is sent more than one measure. To do so, you need to set add to the test +case the parameter `should.type` to the value `'multimeasure'`. -You must define the measure as multimeasure. This is done by defining the `measure` JSON element as an array of -objects. I.E: +You must define the measure as multimeasure. This is done by defining the `measure` JSON element as an array of objects. +I.E: ```javascript measure: { @@ -276,8 +280,8 @@ expectation: { } ``` -Then, a batch request would be sent to the Context Broker containing the different measures. More information about -how the IoT Agent send multimeasures to the Context Broker [here](/doc/api.md#multimeasure-support). +Then, a batch request would be sent to the Context Broker containing the different measures. More information about how +the IoT Agent send multimeasures to the Context Broker [here](/doc/api.md#multimeasure-support). #### Transport @@ -332,7 +336,8 @@ the expectation as an empty object. I.E: ... ``` -Note that this means the CB *must not* receive any payload. In other words, if the CB would receive any payload, the test will fail. +Note that this means the CB _must not_ receive any payload. In other words, if the CB would receive any payload, the +test will fail. ### Debugging automated tests From e07371da683fd847dbbe8ec642759c9ef4e544a7 Mon Sep 17 00:00:00 2001 From: mapedraza <40356341+mapedraza@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:49:32 +0200 Subject: [PATCH 5/7] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fermín Galán Márquez --- lib/services/northBound/deviceGroupAdministrationServer.js | 2 +- test/unit/general/deviceService-test.js | 4 ++-- test/unit/mongodb/mongodb-configGroup-registry-test.js | 2 +- test/unit/mongodb/mongodb-group-registry-test.js | 2 +- test/unit/ngsiv2/general/deviceService-test.js | 4 ++-- test/unit/ngsiv2/general/iotam-autoregistration-test.js | 6 +++--- test/unit/ngsiv2/provisioning/device-group-api-test.js | 2 +- .../device-provisioning-configGroup-api_test.js | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/services/northBound/deviceGroupAdministrationServer.js b/lib/services/northBound/deviceGroupAdministrationServer.js index 2ad2f8a21..99fc2ae40 100644 --- a/lib/services/northBound/deviceGroupAdministrationServer.js +++ b/lib/services/northBound/deviceGroupAdministrationServer.js @@ -38,7 +38,7 @@ const mandatoryHeaders = ['fiware-service', 'fiware-servicepath']; const mandatoryParameters = ['resource', 'apikey']; const constants = require('../../constants'); -// Create new templeate for configuration groups replacing services by CONFIGGROUP_TERM +// Create new template for configuration groups replacing services by CONFIGGROUP_TERM const templateGroup = JSON.parse(JSON.stringify(templateGroupService)); templateGroup.properties[constants.CONFIGGROUP_TERM] = templateGroup.properties.services; delete templateGroup.properties.services; diff --git a/test/unit/general/deviceService-test.js b/test/unit/general/deviceService-test.js index 8078bae31..3b62b4684 100644 --- a/test/unit/general/deviceService-test.js +++ b/test/unit/general/deviceService-test.js @@ -210,7 +210,7 @@ describe('NGSI-v2 - Device Service: utils', function () { async.series([iotAgentLib.clearAll, iotAgentLib.deactivate], done); }); - //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed + // #FIXME1649: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When an existing device tries to be retrieved with retrieveOrCreate()', function () { beforeEach(function (done) { contextBrokerMock = nock('http://192.168.1.1:1026') @@ -277,7 +277,7 @@ describe('NGSI-v2 - Device Service: utils', function () { }); }); - //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed + // #FIXME1649: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When an unexisting device tries to be retrieved for an existing APIKey', function () { beforeEach(function (done) { diff --git a/test/unit/mongodb/mongodb-configGroup-registry-test.js b/test/unit/mongodb/mongodb-configGroup-registry-test.js index 26d1f3d84..a6a945283 100644 --- a/test/unit/mongodb/mongodb-configGroup-registry-test.js +++ b/test/unit/mongodb/mongodb-configGroup-registry-test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U + * Copyright 2024 Telefonica Investigación y Desarrollo, S.A.U * * This file is part of fiware-iotagent-lib * diff --git a/test/unit/mongodb/mongodb-group-registry-test.js b/test/unit/mongodb/mongodb-group-registry-test.js index 2ca1dd4d9..f15303e8e 100644 --- a/test/unit/mongodb/mongodb-group-registry-test.js +++ b/test/unit/mongodb/mongodb-group-registry-test.js @@ -23,7 +23,7 @@ * Modified by: Daniel Calvo - ATOS Research & Innovation */ -// FIXME: parallel tests in mongodb-configGroup-registry-test.js. Remove this file if at the end /iot/services API (now Deprecated) is removed +// #FIXME1649: parallel tests in mongodb-configGroup-registry-test.js. Remove this file if at the end /iot/services API (now Deprecated) is removed /* eslint-disable no-unused-vars */ const iotAgentLib = require('../../../lib/fiware-iotagent-lib'); diff --git a/test/unit/ngsiv2/general/deviceService-test.js b/test/unit/ngsiv2/general/deviceService-test.js index 4680e705f..e42ed3e36 100644 --- a/test/unit/ngsiv2/general/deviceService-test.js +++ b/test/unit/ngsiv2/general/deviceService-test.js @@ -210,7 +210,7 @@ describe('NGSI-v2 - Device Service: utils', function () { nock.cleanAll(); async.series([iotAgentLib.clearAll, iotAgentLib.deactivate], done); }); - //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed + // #FIXME1649: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When an existing device tries to be retrieved with retrieveOrCreate()', function () { beforeEach(function (done) { // This mock does not check the payload since the aim of the test is not to verify @@ -272,7 +272,7 @@ describe('NGSI-v2 - Device Service: utils', function () { }); }); }); - //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed + // #FIXME1649: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When an unexisting device tries to be retrieved for an existing APIKey', function () { beforeEach(function (done) { // This mock does not check the payload since the aim of the test is not to verify diff --git a/test/unit/ngsiv2/general/iotam-autoregistration-test.js b/test/unit/ngsiv2/general/iotam-autoregistration-test.js index 1f15fa952..8a8d0149c 100644 --- a/test/unit/ngsiv2/general/iotam-autoregistration-test.js +++ b/test/unit/ngsiv2/general/iotam-autoregistration-test.js @@ -351,7 +351,7 @@ describe('NGSI-v2 - IoT Manager autoregistration', function () { }); }); - //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed + // #FIXME1649: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When a new service is created in the IoT Agent', function () { beforeEach(function (done) { nock.cleanAll(); @@ -422,7 +422,7 @@ describe('NGSI-v2 - IoT Manager autoregistration', function () { }); }); - //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed + // #FIXME1649: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When a service is removed from the IoT Agent', function () { beforeEach(function (done) { nock.cleanAll(); @@ -493,7 +493,7 @@ describe('NGSI-v2 - IoT Manager autoregistration', function () { }); }); - //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed + // #FIXME1649: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When a new service with static attributes is created in the IoT Agent', function () { beforeEach(function (done) { nock.cleanAll(); diff --git a/test/unit/ngsiv2/provisioning/device-group-api-test.js b/test/unit/ngsiv2/provisioning/device-group-api-test.js index e2871cff7..832837d13 100644 --- a/test/unit/ngsiv2/provisioning/device-group-api-test.js +++ b/test/unit/ngsiv2/provisioning/device-group-api-test.js @@ -23,7 +23,7 @@ /* eslint-disable no-unused-vars */ -// FIXME: parallel tests in device-provisioning-configGroup-api_test.js. +// #FIXME1649: parallel tests in device-provisioning-configGroup-api_test.js. const iotAgentLib = require('../../../../lib/fiware-iotagent-lib'); const _ = require('underscore'); diff --git a/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js b/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js index 82a13a9b9..6248ca7dc 100644 --- a/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js +++ b/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js @@ -1,5 +1,5 @@ /* - * Copyright 2014 Telefonica Investigación y Desarrollo, S.A.U + * Copyright 2024 Telefonica Investigación y Desarrollo, S.A.U * * This file is part of fiware-iotagent-lib * From 24f421ac6ac005da27a2181ec6ecc6ca02f05c81 Mon Sep 17 00:00:00 2001 From: mapedraza <40356341+mapedraza@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:50:47 +0200 Subject: [PATCH 6/7] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fermín Galán Márquez --- test/unit/mongodb/mongodb-service-registry-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/mongodb/mongodb-service-registry-test.js b/test/unit/mongodb/mongodb-service-registry-test.js index 07c1cce8d..0788b2595 100644 --- a/test/unit/mongodb/mongodb-service-registry-test.js +++ b/test/unit/mongodb/mongodb-service-registry-test.js @@ -1,5 +1,5 @@ /* - * Copyright 2015 Telefonica Investigación y Desarrollo, S.A.U + * Copyright 2024 Telefonica Investigación y Desarrollo, S.A.U * * This file is part of fiware-iotagent-lib * @@ -23,7 +23,7 @@ * Modified by: Daniel Calvo - ATOS Research & Innovation */ -// FIXME: parallel tests in mongodb-configGroup-registry-test.js. Remove this file if at the end /iot/services API (now Deprecated) is removed +// #FIXME1649: parallel tests in mongodb-configGroup-registry-test.js. Remove this file if at the end /iot/services API (now Deprecated) is removed /* eslint-disable no-unused-vars */ const iotAgentLib = require('../../../lib/fiware-iotagent-lib'); From 6b5d5cb63194f5e8e267541a7eab3176edf4f246 Mon Sep 17 00:00:00 2001 From: mapedraza <40356341+mapedraza@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:51:48 +0200 Subject: [PATCH 7/7] Add CNR entry --- CHANGES_NEXT_RELEASE | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 353820b12..fbb35ab51 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -4,3 +4,5 @@ - Fix: Allow to send to CB batch update for multimeasures for NGSI-LD (#1623) - Add: new JEXL transformations for including into an array keys that have a certain value: valuePicker and valuePickerMulti - Add: attribute metadata and static attributes metadata added to jexl context (#1630) +- Add /iot/groups API endpoints (as equivalent to /iot/services) (#752) +- Deprecated: /iot/services API routes