From 825fd819ae47546fba4ebd2a61b5cb6d6beebe25 Mon Sep 17 00:00:00 2001 From: Sunny-NEC Date: Fri, 4 Feb 2022 11:47:59 +0530 Subject: [PATCH 1/6] added code for iot/configGroups --- doc/api.md | 140 +- lib/fiware-iotagent-lib.js | 6 +- lib/services/groups/groupRegistryMemory.js | 62 + lib/services/groups/groupRegistryMongoDB.js | 71 + lib/services/groups/groupService.js | 44 +- .../deviceGroupAdministrationServer.js | 59 +- lib/services/northBound/northboundServer.js | 3 +- .../bidirectionalConfigGroup.json | 31 + .../multipleConfigGroupsCreation.json | 44 + .../provisionDuplicateConfigGroup.json | 35 + .../provisionFullConfigGroup.json | 36 + .../provisionFullConfigGroupAlternate.json | 36 + test/unit/general/deviceService-test.js | 152 +- .../general/iotam-autoregistration-test.js | 564 +++++++ .../mongodb-configGroup-registry-test.js | 451 ++++++ .../unit/ngsiv2/general/deviceService-test.js | 109 +- .../general/iotam-autoregistration-test.js | 199 ++- .../plugins/bidirectional-plugin_test.js | 154 +- ...evice-provisioning-configGroup-api_test.js | 1327 +++++++++++++++++ .../singleGroupConfigurationMode-test.js | 320 ++++ 20 files changed, 3730 insertions(+), 113 deletions(-) create mode 100644 test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json 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/general/iotam-autoregistration-test.js create mode 100644 test/unit/mongodb/mongodb-configGroup-registry-test.js create mode 100644 test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js create mode 100644 test/unit/ngsiv2/provisioning/singleGroupConfigurationMode-test.js diff --git a/doc/api.md b/doc/api.md index 1524a370c..588e6119f 100644 --- a/doc/api.md +++ b/doc/api.md @@ -29,7 +29,7 @@ assigned type, its configuration values can be extracted from those of the type. The IoT Agents provide two means to define those service groups: - Static **Type Configuration**: configuring the `ngsi.types` attribute within the `config.js` file. -- Dynamic **Configuration API**: making use of the API URLs in the configuration URI, `/iot/services`. Please, note +- Dynamic **Configuration API**: making use of the API URLs in the configuration URI, `/iot/services` or `/iot/configGroups`. Please, note that the configuration API manage servers under a URL that requires the `server.name` parameter to be set (the name of the IoT Agent we are using). If no name is configured `default` is taken as the default one. @@ -75,42 +75,69 @@ information configured: The table below shows the information held in the service group provisioning resource. The table also contains the correspondence between the API resource fields and the same fields in the database model. -| Payload Field | DB Field | Definition | -| ------------------------------ | ------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `service` | `service` | Service of the devices of this type | -| `subservice` | `subservice` | Subservice of the devices of this type. | -| `resource` | `resource` | string representing the Southbound resource that will be used to assign a type to a device (e.g.: pathname in the southbound port). | -| `apikey` | `apikey` | API Key string. | -| `timestamp` | `timestamp` | Optional flag about whether or not to add the `TimeInstant` attribute to the device entity created, as well as a `TimeInstant` metadata to each attribute, with the current timestamp. With NGSI-LD, the Standard `observedAt` property-of-a-property is created instead. | true | -| `entity_type` | `entity_type` | name of the Entity `type` to assign to the group. | -| `trust` | `trust` | trust token to use for secured access to the Context Broker for this type of devices (optional; only needed for secured scenarios). | -| `cbHost` | `cbHost` | Context Broker connection information. This options can be used to override the global ones for specific types of devices. | -| `lazy` | `lazy` | list of common lazy attributes of the device. For each attribute, its `name` and `type` must be provided. | -| `commands` | `commands` | list of common commands attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. | -| `attributes` | `attributes` | list of common active attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. | -| `static_attributes` | `staticAttributes` | this attributes will be added to all the entities of this group 'as is', additional `metadata` is optional. | -| `internal_attributes` | `internalAttributes` | optional section with free format, to allow specific IoT Agents to store information along with the devices in the Device Registry. | -| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, `legacy` is used as default value. | -| `explicitAttrs` | `explicitAttrs` | optional field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](advanced-topics.md#explicitly-defined-attributes-explicitattrs) | -| `ngsiVersion` | `ngsiVersion` | optional string value used in mixed mode to switch between **NGSI-v2** and **NGSI-LD** payloads. Possible values are: `v2` or `ld`. The default is `v2`. When not running in mixed mode, this field is ignored. | -| `defaultEntityNameConjunction` | `defaultEntityNameConjunction` | optional string value to set default conjunction string used to compose a default `entity_name` when is not provided at device provisioning time. | -| `autoprovision` | `autoprovision` | optional boolean: If `false`, autoprovisioned devices (i.e. devices that are not created with an explicit provision operation but when the first measure arrives) are not allowed in this group. Default (in the case of omitting the field) is `true`. | +| Payload Field | DB Field | Definition | +| ------------------------------ | ------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `service` | `service` | Service of the devices of this type | +| `subservice` | `subservice` | Subservice of the devices of this type. | +| `resource` | `resource` | string representing the Southbound resource that will be used to assign a type to a device (e.g.: pathname in the southbound port). | +| `apikey` | `apikey` | API Key string. | +| `timestamp` | `timestamp` | Optional flag about whether or not to add the `TimeInstant` attribute to the device entity created, as well as a `TimeInstant` metadata to each attribute, with the current timestamp. With NGSI-LD, the Standard `observedAt` property-of-a-property is created instead. | true | +| `entity_type` | `entity_type` | name of the Entity `type` to assign to the group. | +| `trust` | `trust` | trust token to use for secured access to the Context Broker for this type of devices (optional; only needed for secured scenarios). | +| `cbHost` | `cbHost` | Context Broker connection information. This options can be used to override the global ones for specific types of devices. | +| `lazy` | `lazy` | list of common lazy attributes of the device. For each attribute, its `name` and `type` must be provided. | +| `commands` | `commands` | list of common commands attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. | +| `attributes` | `attributes` | list of common active attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. | +| `static_attributes` | `staticAttributes` | this attributes will be added to all the entities of this group 'as is', additional `metadata` is optional. | +| `internal_attributes` | `internalAttributes` | optional section with free format, to allow specific IoT Agents to store information along with the devices in the Device Registry. | +| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, `legacy` is used as default value. | +| `explicitAttrs` | `explicitAttrs` | optional boolean value, to support selective ignore of measures so that IOTA doesn’t progress. If not specified default is `false`. | +| `ngsiVersion` | `ngsiVersion` | optional string value used in mixed mode to switch between **NGSI-v2** and **NGSI-LD** payloads. Possible values are: `v2` or `ld`. The default is `v2`. When not running in mixed mode, this field is ignored. | +| `defaultEntityNameConjunction` | `defaultEntityNameConjunction` | optional string value to set default conjunction string used to compose a default `entity_name` when is not provided at device provisioning time. | +| `autoprovision` | `autoprovision` | optional boolean: If `false`, autoprovisioned devices (i.e. devices that are not created with an explicit provision operation but when the first measure arrives) are not allowed in this group. Default (in the case of omitting the field) is `true`. | ### Service Group Endpoint The following actions are available under the service group endpoint: -##### POST /iot/services +##### POST /iot/configGroups or POST /iot/services Creates a set of service groups for the given service and service path. The service and subservice information will taken from the headers, overwritting any preexisting values. Body params: -- `services`: list of service groups to create. Each one adheres to the service group Model. +- `configGroups`: list of service groups to create. Each one adheres to the service group Model (when `/iot/configGroups` route is used). +- `services`: list of service groups to create. Each one adheres to the service group Model (when `/iot/services` route is used). E.g.: +```json +{ + "configGroups": [ + { + "resource": "/deviceTest", + "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732", + "type": "Light", + "trust": "8970A9078A803H3BL98PINEQRW8342HBAMS", + "cbHost": "http://orion:1026", + "commands": [{ "name": "wheel1", "type": "Wheel" }], + "attributes": [ + { + "name": "luminescence", + "type": "Integer", + "metadata": { + "unitCode": { "type": "Text", "value": "CAL" } + } + } + ], + "lazy": [{ "name": "status", "type": "Boolean" }] + } + ] +} +``` +OR + ```json { "services": [ @@ -143,9 +170,9 @@ Returns: - 400 WRONG_SYNTAX if the body doesn't comply with the schema. - 500 SERVER ERROR if there was any error not contemplated above. -##### GET /iot/services +##### GET /iot/configGroups or GET /iot/services -Retrieves service groups from the database. If the servicepath header has the wildcard expression, `/*`, all the +Retrieves configuration groups from the database. If the servicepath header has the wildcard expression, `/*`, all the subservices for the service are returned. The specific subservice parameters are returned in any other case. Returns: @@ -154,10 +181,10 @@ Returns: - 400 MISSING_HEADERS if any of the mandatory headers is not present. - 500 SERVER ERROR if there was any error not contemplated above. -##### PUT /iot/services +##### PUT /iot/configGroups or PUT /iot/services Modifies the information for a service group configuration, 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 +Takes a service/configuration group body as the payload. The body does not have to be complete: for incomplete bodies, just the existing attributes will be updated E.g.: @@ -175,7 +202,7 @@ Returns: - 400 MISSING_HEADERS if any of the mandatory headers is not present. - 500 SERVER ERROR if there was any error not contemplated above. -##### DELETE /iot/services +##### DELETE /iot/configGroups or DELETE /iot/services Removes a service group configuration from the DB, specified by the `resource` and `apikey` query parameters. @@ -203,47 +230,32 @@ outgoing requests). Note that there is a 1:1 correspondence between payload fields and DB fields (but using a different capitalization, e.g. `service_path` vs. `servicePath`). -### Relationship between service groups and devices - -Devices may be associated to exisiting service groups (or not) based in `apiKey` matching or `type` matching (in the -case `apiKey` matching fails). For instance, let's consider a situation in which a service group has been provisioned -with `type=X`/`apiKey=111` and no other service group has been provisioned. - -- IoT Agent receives an anonymous measure with `apiKey=111`. The matching `apiKey` means the entity inherits from - service group. Device entity has `type=X` and `apiKey=111` -- IoT Agent receives a provisioning request for an explicit device of `type=Y`/`apiKey=111`. The matching `apiKey` - means the entity inherits from service group but type is overridden. Device entity has `type=Y` and `apiKey=111` -- IoT Agent receives a provisioning request for an explicit device of `type=X`/`apiKey=222`. The matching `type` means - the entity inherits from service group but `apiKey` is overridden. Device entity has `type=X` and `apiKey=222`. -- IoT Agent receives a provisioning request for an explicit device of `type=Y`/`apiKey=222`. No matching. Device - entity has `type=Y` and `apiKey=222` and no service group. - ### Device model The table below shows the information held in the Device resource. The table also contains the correspondence between the API resource fields and the same fields in the database model. -| Payload Field | DB Field | Definition | Example of value | -| --------------------- | -------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------- | -| `device_id` | `id` | Device ID that will be used to identify the device. | UO834IO | -| `service` | `service` | Name of the service the device belongs to (will be used in the fiware-service header). | smartGondor | -| `service_path` | `subservice` | Name of the subservice the device belongs to (used in the fiware-servicepath header). | /gardens | -| `entity_name` | `name` | Name of the entity representing the device in the Context Broker | ParkLamplight12 | -| `entity_type` | `type` | Type of the entity in the Context Broker | Lamplights | -| `timezone` | `timezone` | Time zone of the sensor if it has any | America/Santiago | -| `timestamp` | `timestamp` | Optional flag about whether or not to add the `TimeInstant` attribute to the device entity created, as well as a `TimeInstant` metadata to each attribute, with the current timestamp. With NGSI-LD, the Standard `observedAt` property-of-a-property is created instead. | true | -| `apikey` | `apikey` | Optional Apikey key string to use instead of group apikey | 9n4hb1vpwbjozzmw9f0flf9c2 | -| `endpoint` | `endpoint` | Endpoint where the device is going to receive commands, if any. | http://theDeviceUrl:1234/commands | -| `protocol` | `protocol` | Name of the device protocol, for its use with an IoT Manager. | IoTA-UL | -| `transport` | `transport` | Name of the device transport protocol, for the IoT Agents with multiple transport protocols. | MQTT | -| `attributes` | `active` | List of active attributes of the device | `[ { "name": "attr_name", "type": "Text" } ]` | -| `lazy` | `lazy` | List of lazy attributes of the device | `[ { "name": "attr_name", "type": "Text" } ]` | -| `commands` | `commands` | List of commands of the device | `[ { "name": "attr_name", "type": "Text" } ]` | -| `internal_attributes` | `internalAttributes` | List of internal attributes with free format for specific IoT Agent configuration | LWM2M mappings from object URIs to attributes | -| `static_attributes` | `staticAttributes` | List of static attributes to append to the entity. All the updateContext requests to the CB will have this set of attributes appended. | `[ { "name": "attr_name", "type": "Text" } ]` | -| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, legacy is used as default value. | -| `explicitAttrs` | `explicitAttrs` | optional field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](advanced-topics.md#explicitly-defined-attributes-explicitattrs) | (see details in specific section) | -| `ngsiVersion` | `ngsiVersion` | optional string value used in mixed mode to switch between **NGSI-v2** and **NGSI-LD** payloads. The default is `v2`. When not running in mixed mode, this field is ignored. | `v2/ld` | +| Payload Field | DB Field | Definition | Example of value | +| --------------------- | -------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------- | +| `device_id` | `id` | Device ID that will be used to identify the device. | UO834IO | +| `service` | `service` | Name of the service the device belongs to (will be used in the fiware-service header). | smartGondor | +| `service_path` | `subservice` | Name of the subservice the device belongs to (used in the fiware-servicepath header). | /gardens | +| `entity_name` | `name` | Name of the entity representing the device in the Context Broker | ParkLamplight12 | +| `entity_type` | `type` | Type of the entity in the Context Broker | Lamplights | +| `timezone` | `timezone` | Time zone of the sensor if it has any | America/Santiago | +| `timestamp` | `timestamp` | Optional flag about whether or not to add the `TimeInstant` attribute to the device entity created, as well as a `TimeInstant` metadata to each attribute, with the current timestamp. With NGSI-LD, the Standard `observedAt` property-of-a-property is created instead. | true | +| `apikey` | `apikey` | Optional Apikey key string to use instead of group apikey | 9n4hb1vpwbjozzmw9f0flf9c2 | +| `endpoint` | `endpoint` | Endpoint where the device is going to receive commands, if any. | http://theDeviceUrl:1234/commands | +| `protocol` | `protocol` | Name of the device protocol, for its use with an IoT Manager. | IoTA-UL | +| `transport` | `transport` | Name of the device transport protocol, for the IoT Agents with multiple transport protocols. | MQTT | +| `attributes` | `active` | List of active attributes of the device | `[ { "name": "attr_name", "type": "Text" } ]` | +| `lazy` | `lazy` | List of lazy attributes of the device | `[ { "name": "attr_name", "type": "Text" } ]` | +| `commands` | `commands` | List of commands of the device | `[ { "name": "attr_name", "type": "Text" } ]` | +| `internal_attributes` | `internalAttributes` | List of internal attributes with free format for specific IoT Agent configuration | LWM2M mappings from object URIs to attributes | +| `static_attributes` | `staticAttributes` | List of static attributes to append to the entity. All the updateContext requests to the CB will have this set of attributes appended. | `[ { "name": "attr_name", "type": "Text" } ]` | +| `expressionLanguage` | `expresionLanguage` | optional boolean value, to set expression language used to compute expressions, possible values are: legacy or jexl. When not set or wrongly set, legacy is used as default value. | +| `explicitAttrs` | `explicitAttrs` | Boolean value to support selective ignore of measures for device so that IOTA doesn’t progress. If not specified default is `false`. | `true/false` | +| `ngsiVersion` | `ngsiVersion` | optional string value used in mixed mode to switch between **NGSI-v2** and **NGSI-LD** payloads. The default is `v2`. When not running in mixed mode, this field is ignored. | `v2/ld` | #### Attribute lists diff --git a/lib/fiware-iotagent-lib.js b/lib/fiware-iotagent-lib.js index 959989a47..8c76112f1 100644 --- a/lib/fiware-iotagent-lib.js +++ b/lib/fiware-iotagent-lib.js @@ -37,7 +37,6 @@ const iotManager = require('./services/common/iotManagerService'); const contextServer = require('./services/northBound/northboundServer'); const errors = require('./errors'); const constants = require('./constants'); -const request = require('./request-shim'); const logger = require('logops'); const config = require('./commonConfig'); const cluster = require('cluster'); @@ -324,9 +323,10 @@ exports.setDataUpdateHandler = contextServer.setUpdateHandler; exports.setCommandHandler = contextServer.setCommandHandler; exports.setDataQueryHandler = contextServer.setQueryHandler; exports.setConfigurationHandler = contextServer.setConfigurationHandler; +exports.setGroupConfigurationHandler = contextServer.setGroupConfigurationHandler; exports.setRemoveConfigurationHandler = contextServer.setRemoveConfigurationHandler; +exports.setRemoveGroupConfigurationHandler = contextServer.setRemoveGroupConfigurationHandler; exports.setProvisioningHandler = contextServer.setProvisioningHandler; -exports.setUpdatingHandler = contextServer.setUpdatingHandler; exports.setRemoveDeviceHandler = contextServer.setRemoveDeviceHandler; exports.setNotificationHandler = contextServer.setNotificationHandler; exports.addUpdateMiddleware = ngsi.addUpdateMiddleware; @@ -351,7 +351,6 @@ exports.ensureSouthboundDomain = domainUtils.ensureSouthboundDomain; exports.finishSouthBoundTransaction = domainUtils.finishSouthBoundTransaction; exports.requestDomain = domainUtils.requestDomain; exports.regenerateTransid = domainUtils.regenerateTransid; -exports.fillService = domainUtils.fillService; exports.middlewares = middlewares; @@ -374,4 +373,3 @@ exports.constants = constants; exports.logModule = logger; exports.configModule = config; exports.startServer = startServer; -exports.request = request; diff --git a/lib/services/groups/groupRegistryMemory.js b/lib/services/groups/groupRegistryMemory.js index 760fa9663..1a82c975b 100644 --- a/lib/services/groups/groupRegistryMemory.js +++ b/lib/services/groups/groupRegistryMemory.js @@ -87,6 +87,40 @@ function getConfigurationsByService(service) { }); } +/** + * List all the configGroups created in the IoT Agent. The result, passed as a parameter in the callback + * will be an object with two attributes: the services array with the list of groups; and a count + * attribute with the total number of groups in the collection. + * + * @param {Number} service Service for wich all the configurations want to be retrieved. + * @param {Number} limit Maximum number of entries to return. + * @param {Number} offset Number of entries to skip for pagination. + */ +function listConfigGroups(service, limit, offset, callback) { + const result = []; + let skipped = 0; + const filteredGroups = getConfigurationsByService(service); + + for (const i in filteredGroups) { + if (registeredGroups.hasOwnProperty(filteredGroups[i])) { + if (offset && skipped < parseInt(offset, 10)) { + skipped++; + } else { + result.push(registeredGroups[filteredGroups[i]]); + } + + if (limit && result.length === parseInt(limit, 10)) { + break; + } + } + } + + callback(null, { + count: filteredGroups.length, + configGroups: result + }); +} + /** * List all the groups created in the IoT Agent. The result, passed as a parameter in the callback * will be an object with two attributes: the services array with the list of groups; and a count @@ -152,6 +186,32 @@ function findSingleConfigurationMode(service, subservice, callback) { } } +function findConfigGroups(service, subservice, callback) { + if (config.getConfig().singleConfigurationMode === true) { + return findSingleConfigurationMode(service, subservice, callback); + } + const result = []; + + for (const i in registeredGroups) { + if ( + registeredGroups.hasOwnProperty(i) && + registeredGroups[i].service === service && + registeredGroups[i].subservice === subservice + ) { + result.push(registeredGroups[i]); + } + } + + if (result.length > 0) { + return callback(null, { + count: result.length, + configGroups: result + }); + } else { + return callback(new errors.DeviceGroupNotFound(service, subservice)); + } +} + function find(service, subservice, callback) { if (config.getConfig().singleConfigurationMode === true) { return findSingleConfigurationMode(service, subservice, callback); @@ -284,8 +344,10 @@ function remove(id, callback) { exports.create = intoTrans(context, createGroup); exports.list = intoTrans(context, listGroups); +exports.listConfigGroups = intoTrans(context, listConfigGroups); exports.init = intoTrans(context, init); exports.find = intoTrans(context, find); +exports.findConfigGroups = intoTrans(context, findConfigGroups); exports.findBy = intoTrans(context, findBy); exports.findType = intoTrans(context, findBy(['service', 'subservice', 'type'])); exports.findTypeSilently = intoTrans(context, findBy(['service', 'subservice', 'type'])); diff --git a/lib/services/groups/groupRegistryMongoDB.js b/lib/services/groups/groupRegistryMongoDB.js index 8ac17decb..58dd5c36d 100644 --- a/lib/services/groups/groupRegistryMongoDB.js +++ b/lib/services/groups/groupRegistryMongoDB.js @@ -118,6 +118,45 @@ function createGroup(group, callback) { }); } +/** + * List all the configGroups created in the IoT Agent. + * + * @param {Number} service Service for wich all the configurations want to be retrieved. + * @param {Number} limit Maximum number of entries to return. + * @param {Number} offset Number of entries to skip for pagination. + */ +function listConfigGroups(service, limit, offset, callback) { + const condition = {}; + + function toObjectFn(obj) { + return obj.toObject(); + } + + if (service) { + condition.service = service; + } + + const query = Group.model.find(condition).sort(); + + if (limit) { + query.limit(parseInt(limit, 10)); + } + + if (offset) { + query.skip(parseInt(offset, 10)); + } + + async.series([query.exec.bind(query), Group.model.countDocuments.bind(Group.model, condition)], function ( + error, + results + ) { + callback(error, { + count: results[1], + configGroups: results[0].map(toObjectFn) + }); + }); +} + /** * List all the groups created in the IoT Agent. * @@ -181,6 +220,36 @@ function getById(id, callback) { }); } +/** + * List all the configGroups created in the IoT Agent for a service and a subservice. + * + * @param {String} service Service used to filter the groups. + * @param {String} subservice Subservice used to filter the groups. + * @param {Function} callback The callback function. + */ +function findConfigGroups(service, subservice, callback) { + const condition = {}; + + function toObjectFn(obj) { + return obj.toObject(); + } + + condition.service = service; + condition.subservice = subservice; + + const query = Group.model.find(condition).sort(); + + async.series([query.exec.bind(query), Group.model.countDocuments.bind(Group.model, condition)], function ( + error, + results + ) { + callback(error, { + count: results[1], + configGroups: results[0].map(toObjectFn) + }); + }); +} + /** * List all the groups created in the IoT Agent for a service and a subservice. * @@ -301,8 +370,10 @@ function clear(callback) { exports.create = alarmsInt(constants.MONGO_ALARM, intoTrans(context, createGroup)); exports.list = alarmsInt(constants.MONGO_ALARM, intoTrans(context, listGroups)); +exports.listConfigGroups = alarmsInt(constants.MONGO_ALARM, intoTrans(context, listConfigGroups)); exports.init = alarmsInt(constants.MONGO_ALARM, intoTrans(context, init)); exports.find = alarmsInt(constants.MONGO_ALARM, intoTrans(context, find)); +exports.findConfigGroups = alarmsInt(constants.MONGO_ALARM, intoTrans(context, findConfigGroups)); exports.findType = alarmsInt( constants.MONGO_ALARM, intoTrans(context, findBy(['service', 'subservice', 'type', 'apikey'])) diff --git a/lib/services/groups/groupService.js b/lib/services/groups/groupService.js index 89a1db5f4..5b986929f 100644 --- a/lib/services/groups/groupService.js +++ b/lib/services/groups/groupService.js @@ -40,7 +40,7 @@ const context = { * * @param {Object} group Validate the device group */ -function validateGroup(group, callback) { +function validateGroup(endpoint, group, callback) { const validations = []; logger.debug(context, 'validateGroup %j', group); @@ -60,7 +60,13 @@ function validateGroup(group, callback) { } function checkServiceAndSubservice(innerCb) { - config.getGroupRegistry().find(group.service, group.subservice, generateDuplicateHandler(innerCb)); + if (endpoint.includes('configGroups')) { + config + .getGroupRegistry() + .findConfigGroups(group.service, group.subservice, generateDuplicateHandler(innerCb)); + } else { + config.getGroupRegistry().find(group.service, group.subservice, generateDuplicateHandler(innerCb)); + } } function checkMandatoryParams(innerCb) { @@ -105,12 +111,22 @@ function createGroup(groupSet, callback) { const insertions = []; const insertedGroups = []; - logger.debug(context, 'Creating new set of %d services', groupSet.services.length); + if (groupSet.configGroups) { + logger.debug(context, 'Creating new set of %d configGroups', groupSet.configGroups.length); + + for (let i = 0; i < groupSet.configGroups.length; i++) { + insertions.push(async.apply(validateGroup, 'configGroups', groupSet.configGroups[i])); + insertions.push(async.apply(config.getGroupRegistry().create, groupSet.configGroups[i])); + insertedGroups.push(groupSet.configGroups[i]); + } + } else { + logger.debug(context, 'Creating new set of %d services', groupSet.services.length); - for (let i = 0; i < groupSet.services.length; i++) { - insertions.push(async.apply(validateGroup, groupSet.services[i])); - insertions.push(async.apply(config.getGroupRegistry().create, groupSet.services[i])); - insertedGroups.push(groupSet.services[i]); + for (let i = 0; i < groupSet.services.length; i++) { + insertions.push(async.apply(validateGroup, 'services', groupSet.services[i])); + insertions.push(async.apply(config.getGroupRegistry().create, groupSet.services[i])); + insertedGroups.push(groupSet.services[i]); + } } insertions.push(iotManagerService.register); @@ -125,12 +141,16 @@ function createGroup(groupSet, callback) { * @param {Number} limit Maximum number of entries to return. * @param {Number} offset Number of entries to skip for pagination. */ -function listGroups(service, limit, offset, callback) { +function listGroups(endpoint, service, limit, offset, callback) { if (!callback) { callback = service; } - config.getGroupRegistry().list(service, limit, offset, callback); + if (endpoint.includes('configGroups')) { + config.getGroupRegistry().listConfigGroups(service, limit, offset, callback); + } else { + config.getGroupRegistry().list(service, limit, offset, callback); + } } function checkServiceIdentity(service, subservice, deviceGroup, callback) { @@ -242,9 +262,11 @@ function update(service, subservice, resource, apikey, body, callback) { * @param {String} subservice Group subservice name. * @param {String} type Group type (optional). */ -function find(service, subservice, type, callback) { +function find(endpoint, service, subservice, type, callback) { if (type) { config.getGroupRegistry().findType(service, subservice, type, callback); + } else if (endpoint.includes('configGroups')) { + config.getGroupRegistry().findConfigGroups(service, subservice, callback); } else { config.getGroupRegistry().find(service, subservice, callback); } @@ -307,7 +329,7 @@ function getEffectiveApiKey(service, subservice, type, callback) { } } - find(service, subservice, type, handleFindGroup); + find('configGroups', service, subservice, type, handleFindGroup); } exports.create = intoTrans(context, createGroup); diff --git a/lib/services/northBound/deviceGroupAdministrationServer.js b/lib/services/northBound/deviceGroupAdministrationServer.js index 40829394f..b4ce6da42 100644 --- a/lib/services/northBound/deviceGroupAdministrationServer.js +++ b/lib/services/northBound/deviceGroupAdministrationServer.js @@ -29,7 +29,7 @@ const restUtils = require('./restUtils'); const groupService = require('./../groups/groupService'); const async = require('async'); const apply = async.apply; -const templateGroup = require('../../templates/deviceGroup.json'); +const templateConfigGroup = require('../../templates/deviceConfigGroup.json'); let configurationHandler; let removeConfigurationHandler; let configurationMiddleware = []; @@ -37,6 +37,9 @@ const _ = require('underscore'); const mandatoryHeaders = ['fiware-service', 'fiware-servicepath']; const mandatoryParameters = ['resource', 'apikey']; +const templateGroup = JSON.parse(JSON.stringify(templateConfigGroup)); +templateGroup.properties.services = templateGroup.properties.configGroups; +delete templateGroup.properties.configGroups; const apiToInternal = { entity_type: 'type', internal_attributes: 'internalAttributes', @@ -70,6 +73,8 @@ function applyConfigurationHandler(newConfiguration, callback) { if (configurationHandler && newConfiguration) { if (newConfiguration.services) { async.map(newConfiguration.services, configurationHandler, callback); + } else if (newConfiguration.configGroups) { + async.map(newConfiguration.configGroups, configurationHandler, callback); } else { configurationHandler(newConfiguration, callback); } @@ -118,10 +123,18 @@ function applyConfigurationMiddlewares(newConfiguration, callback) { * @param {Function} next Invokes the next middleware in the chain. */ function handleCreateDeviceGroup(req, res, next) { - 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']; - req.body.services[i].subservice = req.headers['fiware-servicepath']; + if (req.body.configGroups) { + for (let i = 0; i < req.body.configGroups.length; i++) { + req.body.configGroups[i] = applyMap(apiToInternal, req.body.configGroups[i]); + req.body.configGroups[i].service = req.headers['fiware-service']; + req.body.configGroups[i].subservice = req.headers['fiware-servicepath']; + } + } else { + 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']; + req.body.services[i].subservice = req.headers['fiware-servicepath']; + } } async.series( @@ -158,6 +171,8 @@ function handleListDeviceGroups(req, res, next) { if (group.services) { translatedGroup.services = group.services.map(applyMap.bind(null, internalToApi)); + } else if (group.configGroups) { + translatedGroup.configGroups = group.configGroups.map(applyMap.bind(null, internalToApi)); } else { translatedGroup = applyMap(internalToApi, group); } @@ -167,9 +182,9 @@ function handleListDeviceGroups(req, res, next) { }; if (req.headers['fiware-servicepath'] === '/*') { - groupService.list(req.headers['fiware-service'], req.query.limit, req.query.offset, listHandler); + groupService.list(req.url, req.headers['fiware-service'], req.query.limit, req.query.offset, listHandler); } else { - groupService.find(req.headers['fiware-service'], req.headers['fiware-servicepath'], null, listHandler); + groupService.find(req.url, req.headers['fiware-service'], req.headers['fiware-servicepath'], null, listHandler); } } @@ -288,6 +303,35 @@ function loadContextRoutes(router) { ); } +function loadConfigContextRoutes(router) { + router.post( + '/iot/configGroups', + restUtils.checkRequestAttributes('headers', mandatoryHeaders), + restUtils.checkBody(templateConfigGroup), + handleCreateDeviceGroup + ); + + router.get( + '/iot/configGroups', + restUtils.checkRequestAttributes('headers', mandatoryHeaders), + handleListDeviceGroups + ); + + router.put( + '/iot/configGroups', + restUtils.checkRequestAttributes('headers', mandatoryHeaders), + restUtils.checkRequestAttributes('query', mandatoryParameters), + handleModifyDeviceGroups + ); + + router.delete( + '/iot/configGroups', + restUtils.checkRequestAttributes('headers', mandatoryHeaders), + restUtils.checkRequestAttributes('query', mandatoryParameters), + handleDeleteDeviceGroups + ); +} + function setConfigurationHandler(newHandler) { configurationHandler = newHandler; } @@ -307,6 +351,7 @@ function clear(callback) { } exports.loadContextRoutes = loadContextRoutes; +exports.loadConfigContextRoutes = loadConfigContextRoutes; exports.setConfigurationHandler = setConfigurationHandler; exports.setRemoveConfigurationHandler = setRemoveConfigurationHandler; exports.addConfigurationProvisionMiddleware = addConfigurationProvisionMiddleware; diff --git a/lib/services/northBound/northboundServer.js b/lib/services/northBound/northboundServer.js index 63c5a1497..0dc8dfa01 100644 --- a/lib/services/northBound/northboundServer.js +++ b/lib/services/northBound/northboundServer.js @@ -31,7 +31,6 @@ const domainUtils = require('../common/domain'); const middlewares = require('../common/genericMiddleware'); const intoTrans = domainUtils.intoTrans; const deviceProvisioning = require('./deviceProvisioningServer'); -const deviceUpdating = require('./deviceProvisioningServer'); const groupProvisioning = require('./deviceGroupAdministrationServer'); const logger = require('logops'); const context = { @@ -88,6 +87,7 @@ function start(config, callback) { contextServer.loadContextRoutes(northboundServer.router); deviceProvisioning.loadContextRoutes(northboundServer.router); groupProvisioning.loadContextRoutes(northboundServer.router); + groupProvisioning.loadConfigContextRoutes(northboundServer.router); northboundServer.app.use(middlewares.handleError); @@ -117,7 +117,6 @@ exports.setNotificationHandler = intoTrans(context, contextServer.setNotificatio exports.setConfigurationHandler = intoTrans(context, groupProvisioning.setConfigurationHandler); exports.setRemoveConfigurationHandler = intoTrans(context, groupProvisioning.setRemoveConfigurationHandler); exports.setProvisioningHandler = intoTrans(context, deviceProvisioning.setProvisioningHandler); -exports.setUpdatingHandler = intoTrans(context, deviceUpdating.setUpdatingHandler); exports.setRemoveDeviceHandler = intoTrans(context, deviceProvisioning.setRemoveDeviceHandler); exports.addDeviceProvisionMiddleware = deviceProvisioning.addDeviceProvisionMiddleware; exports.addConfigurationProvisionMiddleware = groupProvisioning.addConfigurationProvisionMiddleware; diff --git a/test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json b/test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json new file mode 100644 index 000000000..1352c8c53 --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json @@ -0,0 +1,31 @@ +{ + "configGroups": [ + { + "resource": "/deviceTest", + "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732", + "entity_type": "TheLightType", + "commands": [], + "lazy": [], + "attributes": [ + { + "name":"location", + "type":"geo:point", + "expression": "${latitude}, ${longitude}", + "reverse": [ + { + "object_id":"latitude", + "type": "string", + "expression": "${trim(substr(@location, indexOf(@location, \",\") + 1, length(@location)))}" + }, + { + "object_id":"longitude", + "type": "string", + "expression": "${trim(substr(@location, 0, indexOf(@location, \",\")))}" + } + ] + } + ], + "static_attributes": [] + } + ] +} diff --git a/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json b/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json new file mode 100644 index 000000000..2138f2965 --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json @@ -0,0 +1,44 @@ +{ + "configGroups": [ + { + "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..d24da2354 --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json @@ -0,0 +1,35 @@ +{ + "configGroups": [ + { + "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..12ae35334 --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json @@ -0,0 +1,36 @@ +{ + "configGroups": [ + { + "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..004865551 --- /dev/null +++ b/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json @@ -0,0 +1,36 @@ +{ + "configGroups": [ + { + "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 2ae3327f9..f6c7b7a52 100644 --- a/test/unit/general/deviceService-test.js +++ b/test/unit/general/deviceService-test.js @@ -25,9 +25,9 @@ const iotAgentLib = require('../../../lib/fiware-iotagent-lib'); const utils = require('../../tools/utils'); -const request = utils.request; const should = require('should'); const nock = require('nock'); +const request = require('request'); const logger = require('logops'); const async = require('async'); const iotAgentConfig = { @@ -152,6 +152,34 @@ const groupCreation = { 'fiware-servicepath': '/testingPath' } }; +const configGroupCreation = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/configGroups', + method: 'POST', + json: { + configGroups: [ + { + resource: '', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + entity_type: 'TheLightType', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://unexistentHost: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', @@ -165,10 +193,10 @@ let contextBrokerMock; let iotamMock; /* jshint camelcase: false */ -describe('NGSI-v2 - Device Service: utils', function () { +describe('NGSI-v1 - Device Service: utils', function () { beforeEach(function (done) { nock.cleanAll(); - logger.setLevel('ERROR'); + logger.setLevel('FATAL'); iotamMock = nock('http://localhost:8082').post('/protocols').reply(200, {}); iotAgentLib.activate(iotAgentConfig, done); @@ -179,19 +207,28 @@ 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://unexistentHost:1026') .matchHeader('fiware-service', 'testservice') .matchHeader('fiware-servicepath', '/testingPath') - .post('/v2/registrations') - .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + .post('/NGSI9/registerContext') + .reply( + 200, + utils.readExampleFile( + './test/unit/examples/contextAvailabilityResponses/registerProvisionedDeviceSuccess.json' + ) + ); contextBrokerMock .matchHeader('fiware-service', 'testservice') .matchHeader('fiware-servicepath', '/testingPath') - .post('/v2/entities?options=upsert') - .reply(204); + .post('/v1/updateContext') + .reply( + 200, + utils.readExampleFile('./test/unit/examples/contextResponses/createProvisionedDeviceSuccess.json') + ); async.series([request.bind(request, groupCreation), request.bind(request, deviceCreation)], function ( error, @@ -212,19 +249,69 @@ 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://unexistentHost:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/NGSI9/registerContext') + .reply( + 200, + utils.readExampleFile( + './test/unit/examples/contextAvailabilityResponses/registerProvisionedDeviceSuccess.json' + ) + ); + + contextBrokerMock + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v1/updateContext') + .reply( + 200, + utils.readExampleFile('./test/unit/examples/contextResponses/createProvisionedDeviceSuccess.json') + ); + + 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://unexistentHost:1026') .matchHeader('fiware-service', 'testservice') .matchHeader('fiware-servicepath', '/testingPath') - .post('/v2/registrations') - .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + .post('/NGSI9/registerContext') + .reply( + 200, + utils.readExampleFile( + './test/unit/examples/contextAvailabilityResponses/registerProvisionedDeviceSuccess.json' + ) + ); contextBrokerMock .matchHeader('fiware-service', 'testservice') .matchHeader('fiware-servicepath', '/testingPath') - .post('/v2/entities?options=upsert') - .reply(204); + .post('/v1/updateContext') + .reply( + 200, + utils.readExampleFile('./test/unit/examples/contextResponses/createProvisionedDeviceSuccess.json') + ); async.series([request.bind(request, groupCreation)], function (error, results) { done(); @@ -247,6 +334,49 @@ 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://unexistentHost:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/NGSI9/registerContext') + .reply( + 200, + utils.readExampleFile( + './test/unit/examples/contextAvailabilityResponses/registerProvisionedDeviceSuccess.json' + ) + ); + + contextBrokerMock + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v1/updateContext') + .reply( + 200, + utils.readExampleFile('./test/unit/examples/contextResponses/createProvisionedDeviceSuccess.json') + ); + + 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('UNEXISTENT_DEV_AND_GROUP', 'H2332Y909DSF3H346yh20JK092', function ( diff --git a/test/unit/general/iotam-autoregistration-test.js b/test/unit/general/iotam-autoregistration-test.js new file mode 100644 index 000000000..d5d48b3d2 --- /dev/null +++ b/test/unit/general/iotam-autoregistration-test.js @@ -0,0 +1,564 @@ +/* + * 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] + */ + +/* eslint-disable no-unused-vars */ + +const iotAgentLib = require('../../../lib/fiware-iotagent-lib'); +const request = require('request'); +const nock = require('nock'); +const utils = require('../../tools/utils'); +const groupRegistryMemory = require('../../../lib/services/groups/groupRegistryMemory'); +const should = require('should'); +const iotAgentConfig = { + logLevel: 'FATAL', + contextBroker: { + host: '192.168.1.1', + port: '1026' + }, + server: { + port: 4041 + }, + types: { + Light: { + commands: [], + type: 'Light', + lazy: [ + { + name: 'temperature', + type: 'centigrades' + } + ], + attributes: [ + { + name: 'pressure', + type: 'Hgmm' + } + ] + } + }, + providerUrl: 'http://smartgondor.com', + deviceRegistrationDuration: 'P1M', + iotManager: { + host: 'mockediotam.com', + port: 9876, + path: '/protocols', + protocol: 'GENERIC_PROTOCOL', + description: 'A generic protocol', + agentPath: '/iot' + }, + defaultResource: '/iot/d' +}; +const groupCreation = { + service: 'theservice', + subservice: 'theSubService', + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + type: 'SensorMachine', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://unexistentHost:1026', + commands: [ + { + name: 'wheel1', + type: 'Wheel' + } + ], + lazy: [ + { + name: 'luminescence', + type: 'Lumens' + } + ], + attributes: [ + { + name: 'status', + type: 'Boolean' + } + ] +}; +const optionsCreation = { + url: 'http://localhost:4041/iot/services', + method: 'POST', + json: { + services: [ + { + 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 configGroupCreation = { + url: 'http://localhost:4041/iot/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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', + json: { + services: [ + { + 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 configGroupCreationStatic = { + url: 'http://localhost:4041/iot/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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', + json: {}, + headers: { + 'fiware-service': 'theservice', + 'fiware-servicepath': 'theSubService' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; +const configGroupDelete = { + url: 'http://localhost:4041/iot/configGroups', + method: 'DELETE', + json: {}, + headers: { + 'fiware-service': 'theservice', + 'fiware-servicepath': 'theSubService' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; +let iotamMock; + +describe('NGSI-v1 - IoT Manager autoregistration', function () { + describe('When the IoT Agent is started without a "iotManager" config parameter and empty services', function () { + beforeEach(function () { + 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')); + }); + + afterEach(function (done) { + iotAgentLib.deactivate(done); + }); + + it('should register itself to the provided IoT Manager URL', function (done) { + iotAgentLib.activate(iotAgentConfig, function (error) { + should.not.exist(error); + iotamMock.done(); + done(); + }); + }); + }); + + describe('When the IoT Agents is started with "iotManager" config with missing attributes', function () { + beforeEach(function () { + nock.cleanAll(); + + delete iotAgentConfig.providerUrl; + + 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')); + }); + + afterEach(function () { + iotAgentConfig.providerUrl = 'http://smartgondor.com'; + }); + + it('should fail with a MISSING_CONFIG_PARAMS error', function (done) { + iotAgentLib.activate(iotAgentConfig, function (error) { + should.exist(error); + error.name.should.equal('MISSING_CONFIG_PARAMS'); + done(); + }); + }); + }); + + describe('When the IoT Agents is started with "iotManager" config and multiple services', 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')); + + groupRegistryMemory.create(groupCreation, done); + }); + + afterEach(function (done) { + groupRegistryMemory.clear(function () { + iotAgentLib.deactivate(done); + }); + }); + + it('should send all the service information to the IoT Manager in the registration', function (done) { + iotAgentLib.activate(iotAgentConfig, function (error) { + 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 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(optionsCreation, function (error, result, body) { + should.not.exist(error); + iotamMock.done(); + done(); + }); + }); + }); + + 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(); + + 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(optionsDelete, function (error, result, body) { + should.not.exist(error); + iotamMock.done(); + done(); + }); + }); + }); + + 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(); + + 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(optionsCreationStatic, function (error, result, body) { + should.not.exist(error); + iotamMock.done(); + done(); + }); + }); + }); + + 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/mongodb/mongodb-configGroup-registry-test.js b/test/unit/mongodb/mongodb-configGroup-registry-test.js new file mode 100644 index 000000000..723eacaa9 --- /dev/null +++ b/test/unit/mongodb/mongodb-configGroup-registry-test.js @@ -0,0 +1,451 @@ +/* + * 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 async = require('async'); +const request = require('request'); +const should = require('should'); +const iotAgentConfig = { + logLevel: 'FATAL', + contextBroker: { + host: '192.168.1.1', + port: '1026' + }, + server: { + name: 'testAgent', + port: 4041, + 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/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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/configGroups', + method: 'DELETE', + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; +const optionsList = { + url: 'http://localhost:4041/iot/configGroups', + method: 'GET', + json: {}, + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/*' + } +}; +const optionsUpdate = { + url: 'http://localhost:4041/iot/configGroups', + 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/configGroups', + 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 = { configGroups: [] }; + optionsCreation3.json = { configGroups: [] }; + + optionsCreation2.json.configGroups[0] = _.clone(optionsCreation.json.configGroups[0]); + optionsCreation3.json.configGroups[0] = _.clone(optionsCreation.json.configGroups[0]); + + optionsCreation2.json.configGroups[0].apikey = 'qwertyuiop'; + optionsCreation3.json.configGroups[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/configGroups', + 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 = { configGroups: [] }; + optionsCreationList[i].json.configGroups[0] = _.clone(optionsCreation.json.configGroups[0]); + optionsCreationList[i].json.configGroups[0].apikey = 'qwertyuiop' + i; + creationFns.push(async.apply(request, optionsCreationList[i])); + } + + async.series(creationFns, done); + }); + + it('should return the appropriate count of configGroups', 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.configGroups); + should.exist(body.configGroups.length); + body.configGroups.length.should.equal(1); + body.configGroups[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 = { configGroups: [] }; + optionsCreationList[i].json.configGroups[0] = _.clone(optionsCreation.json.configGroups[0]); + optionsCreationList[i].json.configGroups[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.configGroups); + should.exist(body.configGroups.length); + body.configGroups.length.should.equal(10); + done(); + }); + }); + }); +}); diff --git a/test/unit/ngsiv2/general/deviceService-test.js b/test/unit/ngsiv2/general/deviceService-test.js index 415761239..23f55faa6 100644 --- a/test/unit/ngsiv2/general/deviceService-test.js +++ b/test/unit/ngsiv2/general/deviceService-test.js @@ -27,10 +27,9 @@ const iotAgentLib = require('../../../../lib/fiware-iotagent-lib'); const utils = require('../../../tools/utils'); -const request = utils.request; const should = require('should'); const nock = require('nock'); - +const request = require('request'); const logger = require('logops'); const async = require('async'); const iotAgentConfig = { @@ -155,6 +154,34 @@ const groupCreation = { 'fiware-servicepath': '/testingPath' } }; +const configGroupCreation = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/configGroups', + method: 'POST', + json: { + configGroups: [ + { + resource: '', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732', + entity_type: 'TheLightType', + trust: '8970A9078A803H3BL98PINEQRW8342HBAMS', + cbHost: 'http://unexistentHost: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,6 +209,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) { // This mock does not check the payload since the aim of the test is not to verify @@ -193,12 +221,42 @@ describe('NGSI-v2 - Device Service: utils', function () { .post('/v2/entities?options=upsert') .reply(204); - async.series( - [utils.request.bind(utils.request, groupCreation), utils.request.bind(utils.request, deviceCreation)], - function (error, results) { - done(); - } - ); + async.series([request.bind(request, groupCreation), 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(); + }); + }); + }); + + 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://unexistenthost:1026') + .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) { @@ -212,6 +270,39 @@ describe('NGSI-v2 - Device Service: utils', function () { }); }); + //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 + // device provisioning functionality. Appropriate verification is done in tests under + // provisioning folder + contextBrokerMock = nock('http://unexistenthost:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + async.series([request.bind(request, groupCreation)], 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 existing APIKey', function () { beforeEach(function (done) { // This mock does not check the payload since the aim of the test is not to verify @@ -223,7 +314,7 @@ describe('NGSI-v2 - Device Service: utils', function () { .post('/v2/entities?options=upsert') .reply(204); - async.series([utils.request.bind(utils.request, groupCreation)], function (error, results) { + async.series([request.bind(request, configGroupCreation)], function (error, results) { done(); }); }); diff --git a/test/unit/ngsiv2/general/iotam-autoregistration-test.js b/test/unit/ngsiv2/general/iotam-autoregistration-test.js index 397cd6adc..5e9aa6ab3 100644 --- a/test/unit/ngsiv2/general/iotam-autoregistration-test.js +++ b/test/unit/ngsiv2/general/iotam-autoregistration-test.js @@ -24,10 +24,9 @@ /* eslint-disable no-unused-vars */ const iotAgentLib = require('../../../../lib/fiware-iotagent-lib'); - +const request = require('request'); const nock = require('nock'); const utils = require('../../../tools/utils'); -const request = utils.request; const groupRegistryMemory = require('../../../../lib/services/groups/groupRegistryMemory'); const should = require('should'); const iotAgentConfig = { @@ -133,6 +132,43 @@ const optionsCreation = { 'fiware-servicepath': 'theSubService' } }; +const configGroupCreation = { + url: 'http://localhost:4041/iot/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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', @@ -171,6 +207,44 @@ const optionsCreationStatic = { 'fiware-servicepath': 'theSubService' } }; +const configGroupCreationStatic = { + url: 'http://localhost:4041/iot/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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', @@ -184,6 +258,19 @@ const optionsDelete = { apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' } }; +const configGroupDelete = { + url: 'http://localhost:4041/iot/configGroups', + method: 'DELETE', + json: {}, + headers: { + 'fiware-service': 'theservice', + 'fiware-servicepath': 'theSubService' + }, + qs: { + resource: '/deviceTest', + apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732' + } +}; let iotamMock; describe('NGSI-v2 - IoT Manager autoregistration', function () { @@ -262,6 +349,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(); @@ -297,6 +385,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(); @@ -332,6 +456,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(); @@ -366,4 +526,39 @@ 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/plugins/bidirectional-plugin_test.js b/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js index a7dbb04e8..9fd9ad806 100644 --- a/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +++ b/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js @@ -27,11 +27,10 @@ const iotAgentLib = require('../../../../lib/fiware-iotagent-lib'); const utils = require('../../../tools/utils'); -const request = utils.request; const should = require('should'); const logger = require('logops'); const nock = require('nock'); - +const request = require('request'); let contextBrokerMock; const iotAgentConfig = { contextBroker: { @@ -148,7 +147,7 @@ describe('NGSI-v2 - Bidirectional data plugin', function () { contextBrokerMock .matchHeader('fiware-service', 'smartgondor') .matchHeader('fiware-servicepath', '/gardens') - .delete('/v2/subscriptions/51c0ac9ed714fb3b37d7d5a8', '') + .delete('/v2/subscriptions/51c0ac9ed714fb3b37d7d5a8') .reply(204); }); @@ -270,6 +269,7 @@ describe('NGSI-v2 - Bidirectional data plugin', function () { }); }); + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When a new Group provisioning request arrives with bidirectional attributes', function () { const provisionGroup = { url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/services', @@ -324,6 +324,61 @@ describe('NGSI-v2 - Bidirectional data plugin', function () { }); }); + describe('When a new configGroup provisioning request arrives with bidirectional attributes', function () { + const provisionGroup = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/configGroups', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const provisionDevice = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionDeviceBidirectionalGroup.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function () { + contextBrokerMock + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post( + '/v2/subscriptions', + utils.readExampleFile( + './test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalSubscriptionRequest.json' + ) + ) + .reply(201, null, { Location: '/v2/subscriptions/51c0ac9ed714fb3b37d7d5a8' }); + + contextBrokerMock + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post( + '/v2/entities?options=upsert', + utils.readExampleFile('./test/unit/ngsiv2/examples/contextRequests/createBidirectionalDevice.json') + ) + .reply(204); + }); + it('should subscribe to the modification of the combined attribute with all the variables', function (done) { + request(provisionGroup, function (error, response, body) { + request(provisionDevice, function (error, response, body) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + }); + }); + + //FIXME: this test will be removed if at the end /iot/services API (now Deprecated) is removed describe('When a notification arrives for a bidirectional attribute in a Configuration Group', function () { const provisionGroup = { url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/services', @@ -416,6 +471,99 @@ describe('NGSI-v2 - Bidirectional data plugin', function () { }); }); }); + + describe('When a notification arrives for a bidirectional attribute in a Configuration Group', function () { + const provisionGroup = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/configGroups', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json'), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const notificationOptions = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/notify', + method: 'POST', + json: utils.readExampleFile( + './test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalNotification.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + const provisionDevice = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile( + './test/unit/examples/deviceProvisioningRequests/provisionDeviceBidirectionalGroup.json' + ), + headers: { + 'fiware-service': 'smartgondor', + 'fiware-servicepath': '/gardens' + } + }; + + beforeEach(function () { + contextBrokerMock + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post( + '/v2/subscriptions', + utils.readExampleFile( + './test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalSubscriptionRequest.json' + ) + ) + .reply(201, null, { Location: '/v2/subscriptions/51c0ac9ed714fb3b37d7d5a8' }); + + contextBrokerMock + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post( + '/v2/entities?options=upsert', + utils.readExampleFile('./test/unit/ngsiv2/examples/contextRequests/createBidirectionalDevice.json') + ) + .reply(204); + }); + + afterEach(function () { + iotAgentLib.setNotificationHandler(); + }); + + it('should return the transformed values', function (done) { + let transformedHandler = false; + + function mockedHandler(device, values, callback) { + let latitudeFound = false; + let longitudeFound = false; + + for (let i = 0; i < values.length; i++) { + if (values[i].name === 'latitude' && values[i].type === 'string' && values[i].value === '-9.6') { + latitudeFound = true; + } + + if (values[i].name === 'longitude' && values[i].type === 'string' && values[i].value === '12.4') { + longitudeFound = true; + } + } + + transformedHandler = values.length >= 2 && longitudeFound && latitudeFound; + callback(); + } + + iotAgentLib.setNotificationHandler(mockedHandler); + + request(provisionGroup, function (error, response, body) { + request(provisionDevice, function (error, response, body) { + request(notificationOptions, function (error, response, body) { + transformedHandler.should.equal(true); + done(); + }); + }); + }); + }); + }); }); describe('NGSI-v2 - Bidirectional data plugin and CB is defined using environment variables', function () { 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..b8590afdc --- /dev/null +++ b/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js @@ -0,0 +1,1327 @@ +/* + * 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 = require('request'); +const moment = require('moment'); +let contextBrokerMock; +const iotAgentConfig = { + logLevel: 'FATAL', + contextBroker: { + host: '192.168.1.1', + port: '1026', + ngsiVersion: 'v2' + }, + server: { + port: 4041, + 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' }); + + contextBrokerMock + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post( + '/v2/entities?options=upsert', + utils.readExampleFile('./test/unit/ngsiv2/examples/contextRequests/createProvisionedDevice.json') + ) + .reply(204); + }); + + 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 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(); + 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/createTimeinstantDevice.json') + ) + .reply(204); + + 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(); + 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/createTimeinstantDevice.json') + ) + .reply(204); + + 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(); + 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/createExplicitAttrsDevice.json') + ) + .reply(204); + + 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(); + 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 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 value provided in configuration', 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(false); + 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/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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 with explicitAttrs value 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].explicitAttrs); + results.devices[0].explicitAttrs.should.equal(true); + 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/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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 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[0].name.should.equal('bootstrapServer'); + 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/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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(2); + 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/configGroups', + method: 'POST', + json: { + configGroups: [ + { + 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 () { + iotAgentConfig.appendMode = false; + iotAgentLib.activate(iotAgentConfig, done); + }); + }); + + afterEach(function () { + iotAgentConfig.appendMode = false; + }); + + 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/createAutoprovisionDevice.json') + ) + .reply(204); + 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 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(); + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartgondor') + .matchHeader('fiware-servicepath', '/gardens') + .post('/v2/entities?options=upsert', function (body) { + const expectedBody = utils.readExampleFile( + './test/unit/ngsiv2/examples/contextRequests/createTimeInstantMinimumDevice.json' + ); + if (!body.TimeInstant.value) { + return false; + } else if (moment(body.TimeInstant.value, 'YYYY-MM-DDTHH:mm:ss.SSSZ').isValid()) { + const timeInstantDiff = moment().diff(body.TimeInstant.value, 'milliseconds'); + if (timeInstantDiff < 500) { + delete body.TimeInstant; + + return JSON.stringify(body) === JSON.stringify(expectedBody); + } + + return false; + } else { + return false; + } + }) + .reply(204); + + 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 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(); + 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 send the appropriate requests to the Context Broker', function (done) { + request(options, function (error, response, body) { + contextBrokerMock.done(); + 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(); + 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/createGeopointProvisionedDevice.json' + ) + ) + .reply(204); + + done(); + }); + + it('should send the appropriate initial values to the Context Broker', function (done) { + request(options, function (error, response, body) { + contextBrokerMock.done(); + 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(); + 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/createDatetimeProvisionedDevice.json' + ) + ) + .reply(204); + + done(); + }); + + it('should send the appropriate initial values to the Context Broker', function (done) { + request(options, function (error, response, body) { + contextBrokerMock.done(); + 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' }); + + // 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') + .replyWithError({ message: 'Description of the error', code: 'STRING_CODE' }); + + done(); + }); + + it('should return a valid return code', function (done) { + request(options, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(500); + + 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' }); + + // 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') + .replyWithError({ message: 'Description of the error', code: 123456789 }); + + 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(500); + + 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(); + }); + }); + }); +}); diff --git a/test/unit/ngsiv2/provisioning/singleGroupConfigurationMode-test.js b/test/unit/ngsiv2/provisioning/singleGroupConfigurationMode-test.js new file mode 100644 index 000000000..7e01abd8b --- /dev/null +++ b/test/unit/ngsiv2/provisioning/singleGroupConfigurationMode-test.js @@ -0,0 +1,320 @@ +/* + * Copyright 2016 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'); +let contextBrokerMock; +const request = require('request'); +const iotAgentConfig = { + logLevel: 'FATAL', + contextBroker: { + host: '192.168.1.1', + port: '1026', + ngsiVersion: 'v2' + }, + server: { + port: 4041, + baseRoot: '/' + }, + types: {}, + service: 'smartgondor', + singleConfigurationMode: true, + subservice: 'gardens', + providerUrl: 'http://smartgondor.com' +}; +const groupCreation = { + url: 'http://localhost:4041/iot/services', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/groupProvisioningRequests/provisionFullGroup.json'), + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } +}; +const deviceCreation = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionNewDevice.json'), + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } +}; + +describe('NGSI-v2 - Provisioning API: Single service mode', function () { + beforeEach(function (done) { + nock.cleanAll(); + + iotAgentLib.activate(iotAgentConfig, function () { + iotAgentLib.clearAll(done); + }); + }); + + afterEach(function (done) { + nock.cleanAll(); + iotAgentLib.setProvisioningHandler(); + iotAgentLib.deactivate(done); + }); + + describe('When a new configuration arrives to an already configured subservice', function () { + const groupCreationDuplicated = { + url: 'http://localhost:4041/iot/services', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/groupProvisioningRequests/provisionDuplicateGroup.json'), + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } + }; + + beforeEach(function (done) { + request(groupCreation, done); + }); + + it('should raise a DUPLICATE_GROUP error', function (done) { + request(groupCreationDuplicated, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(409); + should.exist(body.name); + body.name.should.equal('DUPLICATE_GROUP'); + done(); + }); + }); + }); + describe('When a device is provisioned with an ID that already exists in the configuration', function () { + const deviceCreationDuplicated = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionDuplicatedDev.json'), + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } + }; + + beforeEach(function (done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://unexistentHost:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .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', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + request(groupCreation, function (error) { + request(deviceCreation, function (error, response, body) { + done(); + }); + }); + }); + + it('should raise a DUPLICATE_DEVICE_ID error', function (done) { + request(deviceCreationDuplicated, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(409); + should.exist(body.name); + body.name.should.equal('DUPLICATE_DEVICE_ID'); + done(); + }); + }); + }); + describe('When a device is provisioned with an ID that exists globally but not in the configuration', function () { + const alternativeDeviceCreation = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionNewDevice.json'), + headers: { + 'fiware-service': 'AlternateService', + 'fiware-servicepath': '/testingPath' + } + }; + const alternativeGroupCreation = { + url: 'http://localhost:4041/iot/services', + method: 'POST', + json: utils.readExampleFile('./test/unit/examples/groupProvisioningRequests/provisionFullGroup.json'), + headers: { + 'fiware-service': 'AlternateService', + 'fiware-servicepath': '/testingPath' + } + }; + + beforeEach(function (done) { + nock.cleanAll(); + + 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' }); + + // 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', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'AlternateService') + .matchHeader('fiware-servicepath', '/testingPath') + .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', 'AlternateService') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + request(groupCreation, function (error) { + request(deviceCreation, function (error, response, body) { + request(alternativeGroupCreation, function (error, response, body) { + done(); + }); + }); + }); + }); + + it('should return a 201 OK', function (done) { + request(alternativeDeviceCreation, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + done(); + }); + }); + }); + describe('When a device is provisioned without a type and with a default configuration type', function () { + const getDevice = { + url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices/Light1', + method: 'GET', + headers: { + 'fiware-service': 'testservice', + 'fiware-servicepath': '/testingPath' + } + }; + let oldType; + + beforeEach(function (done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://unexistentHost:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .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', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post('/v2/entities?options=upsert') + .reply(204); + + oldType = deviceCreation.json.devices[0].entity_type; + delete deviceCreation.json.devices[0].entity_type; + request(groupCreation, done); + }); + + afterEach(function () { + deviceCreation.json.devices[0].entity_type = oldType; + }); + + it('should be provisioned with the default type', function (done) { + request(deviceCreation, function (error, response, body) { + request(getDevice, function (error, response, body) { + const parsedBody = JSON.parse(body); + + parsedBody.entity_type.should.equal('SensorMachine'); + + done(); + }); + }); + }); + }); + describe('When a device is provisioned for a configuration', function () { + beforeEach(function (done) { + nock.cleanAll(); + contextBrokerMock = nock('http://unexistentHost:1026') + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post( + '/v2/registrations', + utils.readExampleFile( + './test/unit/ngsiv2/examples' + + '/contextAvailabilityRequests/registerProvisionedDeviceWithGroup.json' + ) + ) + .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584d' }); + + contextBrokerMock + .matchHeader('fiware-service', 'testservice') + .matchHeader('fiware-servicepath', '/testingPath') + .post( + '/v2/entities?options=upsert', + utils.readExampleFile( + './test/unit/ngsiv2/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json' + ) + ) + .reply(204); + + request(groupCreation, done); + }); + + it('should not raise any error', function (done) { + request(deviceCreation, function (error, response, body) { + should.not.exist(error); + response.statusCode.should.equal(201); + done(); + }); + }); + + it('should send the mixed data to the Context Broker', function (done) { + request(deviceCreation, function (error, response, body) { + contextBrokerMock.done(); + done(); + }); + }); + }); +}); From 7888d9171a4a3cb9156cab5c21c6712fdd195562 Mon Sep 17 00:00:00 2001 From: Sunny-NEC <75657997+Sunny-NEC@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:05:36 +0530 Subject: [PATCH 2/6] Delete bidirectionalConfigGroup.json --- .../bidirectionalConfigGroup.json | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json diff --git a/test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json b/test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json deleted file mode 100644 index 1352c8c53..000000000 --- a/test/unit/examples/groupProvisioningRequests/bidirectionalConfigGroup.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "configGroups": [ - { - "resource": "/deviceTest", - "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732", - "entity_type": "TheLightType", - "commands": [], - "lazy": [], - "attributes": [ - { - "name":"location", - "type":"geo:point", - "expression": "${latitude}, ${longitude}", - "reverse": [ - { - "object_id":"latitude", - "type": "string", - "expression": "${trim(substr(@location, indexOf(@location, \",\") + 1, length(@location)))}" - }, - { - "object_id":"longitude", - "type": "string", - "expression": "${trim(substr(@location, 0, indexOf(@location, \",\")))}" - } - ] - } - ], - "static_attributes": [] - } - ] -} From a1e8e5094c4369ae7c3a8db4ff32eb6e55dc4e72 Mon Sep 17 00:00:00 2001 From: Sunny-NEC <75657997+Sunny-NEC@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:10:50 +0530 Subject: [PATCH 3/6] Delete multipleConfigGroupsCreation.json --- .../multipleConfigGroupsCreation.json | 44 ------------------- 1 file changed, 44 deletions(-) delete mode 100644 test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json diff --git a/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json b/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json deleted file mode 100644 index 2138f2965..000000000 --- a/test/unit/examples/groupProvisioningRequests/multipleConfigGroupsCreation.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "configGroups": [ - { - "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": [] - } - ] -} From c68ab9cc344b3b1e323346852da818885ec2c5fa Mon Sep 17 00:00:00 2001 From: Sunny-NEC <75657997+Sunny-NEC@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:13:47 +0530 Subject: [PATCH 4/6] Delete provisionDuplicateConfigGroup.json --- .../provisionDuplicateConfigGroup.json | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json diff --git a/test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json b/test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json deleted file mode 100644 index d24da2354..000000000 --- a/test/unit/examples/groupProvisioningRequests/provisionDuplicateConfigGroup.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "configGroups": [ - { - "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" - } - ] - } - ] -} From 42d93ca8c89d6012f56fe380d3264d02058a0d4f Mon Sep 17 00:00:00 2001 From: Sunny-NEC <75657997+Sunny-NEC@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:15:59 +0530 Subject: [PATCH 5/6] Delete provisionFullConfigGroup.json --- .../provisionFullConfigGroup.json | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json diff --git a/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json b/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json deleted file mode 100644 index 12ae35334..000000000 --- a/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroup.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "configGroups": [ - { - "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" - } - ] - } - ] -} From 437986beaaa1d1a4a4955521618dd71bda921095 Mon Sep 17 00:00:00 2001 From: Sunny-NEC <75657997+Sunny-NEC@users.noreply.github.com> Date: Fri, 4 Feb 2022 12:18:51 +0530 Subject: [PATCH 6/6] Delete provisionFullConfigGroupAlternate.json --- .../provisionFullConfigGroupAlternate.json | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json diff --git a/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json b/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json deleted file mode 100644 index 004865551..000000000 --- a/test/unit/examples/groupProvisioningRequests/provisionFullConfigGroupAlternate.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "configGroups": [ - { - "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" - } - ] - } - ] -}