diff --git a/CHANGES_NEXT_RELEASE b/CHANGES_NEXT_RELEASE index 642b97252..74a016508 100644 --- a/CHANGES_NEXT_RELEASE +++ b/CHANGES_NEXT_RELEASE @@ -3,5 +3,8 @@ Add NGSIv2 metadata support to device provisioned attributes Fix: Error message when sending measures with unknown/undefined attribute Add Null check within executeWithSecurity() to avoid crash (#829) Basic NGSI-LD active measures support (#841) +Add GeoJSON and DateTime, unitCode and observedAt NGSI-LD support (#843) +- The NGSI v2 `TimeInstant` element has been mapped onto the NGSI-LD `observedAt` property +- The NGSI v2 `metadata.unitCode` attribute has been mapped onto the NGSI-LD `unitCode` property Add NGSIv2 metadata support to attributeAlias plugin. Add mongodb authentication: IOTA_MONGO_USER, IOTA_MONGO_PASSWORD and IOTA_MONGO_AUTH_SOURCE (#844) diff --git a/doc/advanced-topics.md b/doc/advanced-topics.md index bf39b4066..2022598e8 100644 --- a/doc/advanced-topics.md +++ b/doc/advanced-topics.md @@ -83,6 +83,50 @@ e.g.: } ``` +#### NGSI-LD data and metadata considerations + +When provisioning devices for an NGSI-LD Context Broker, `type` values should typically correspond to one of the +following: + +- `Property`, `Relationship`, `Geoproperty` +- Native JSON types (e.g. `String`, `Boolean`, `Float` , `Integer` `Number`) +- Temporal Properties (e.g. `Datetime`, `Date` , `Time`) +- GeoJSON types (e.g `Point`, `LineString`, `Polygon`, `MultiPoint`, `MultiLineString`, `MultiPolygon`) + +Most NGSI-LD attributes are sent to the Context Broker as _properties_. If a GeoJSON type or native JSON type is +defined, the data will be converted to the appropriate type. Temporal properties should always be expressed in UTC, +using ISO 8601. This ISO 8601 conversion is applied automatically for the `observedAt` _property-of-a-property_ metadata +where present. + +Data for any attribute defined as a _relationship_ must be a valid URN. + +Note that when the `unitCode` metadata attribute is supplied in the provisioning data under NGSI-LD, the standard +`unitCode` _property-of-a-property_ `String` attribute is created. + +Other unrecognised `type` attributes will be passed as NGSI-LD data using the following JSON-LD format: + +```json + "": { + "type" : "Property", + "value": { + "@type": "", + "@value": { string or object} + } + } +``` + +`null` values will be passed in the following format: + +```json + "": { + "type" : "Property", + "value": { + "@type": "Intangible", + "@value": null + } + } +``` + ### Data mapping plugins The IoT Agent Library provides a plugin mechanism in order to facilitate reusing code that makes small transformations @@ -138,7 +182,7 @@ The library provides some plugins out of the box, in the `dataPlugins` collectio use the `addQueryMiddleware` and `addUpdateMiddleware` functions with the selected plugin, as in the example: ```javascript -var iotaLib = require('iotagent-node-lib'); +var iotaLib = require("iotagent-node-lib"); iotaLib.addUpdateMiddleware(iotaLib.dataPlugins.compressTimestamp.update); iotaLib.addQueryMiddleware(iotaLib.dataPlugins.compressTimestamp.query); @@ -166,8 +210,9 @@ events in the IoT Agent with the configured type name will be marked as events. ##### Timestamp Processing Plugin (timestampProcess) -This plugin processes the entity attributes looking for a TimeInstant attribute. If one is found, the plugin add a -TimeInstant attribute as metadata for every other attribute in the same request. +This plugin processes the entity attributes looking for a `TimeInstant` attribute. If one is found, for NGSI-v1/NGSIv2, +the plugin adds a `TimeInstant` attribute as metadata for every other attribute in the same request. With NGSI-LD, the +Standard `observedAt` property-of-a-property is used instead. ##### Expression Translation plugin (expressionTransformation) diff --git a/doc/api.md b/doc/api.md index f3fb4e73d..0b00c73f2 100644 --- a/doc/api.md +++ b/doc/api.md @@ -203,25 +203,25 @@ Note that there is a 1:1 correspondence between payload fields and DB fields (bu 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 addthe TimeInstant attribute to the device entity created, as well as a TimeInstant metadata to each attribute, with the current timestamp | true | -| `apikey` | `apikey` | Optional Apikey key string to use instead of group apikey | +| 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" } ]` | +| `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" } ]` | #### Attribute lists diff --git a/doc/architecture.md b/doc/architecture.md index db94266da..d172617dd 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -32,7 +32,7 @@ basis preprovisioning the devices). Device measures can have three different beh The following sequence diagram shows the different NGSI interactions an IoT Agent makes with the Context Broker, explained in the following subsections (using the example of a OMA Lightweight M2M device). -![General ](./img/ngsiInteractions.png 'NGSI Interactions') +![General ](./img/ngsiInteractions.png "NGSI Interactions") Be aware that the IoT Agents are only required to support NGSI10 operations `updateContext` and `queryContext` in their standard formats (currently in JSON format; XML deprecated) but will not answer to NGSI9 operations (or NGSI convenience @@ -207,21 +207,22 @@ the concrete IoT Agent implementations will be to map between the native device The following figure offers a graphical example of how a COAP IoT Agent work, ordered from the registration of the device to a command update to the device. -![General ](./img/iotAgentLib.png 'Architecture Overview') +![General ](./img/iotAgentLib.png "Architecture Overview") ### The `TimeInstant` element As part of the device to entity mapping process the IoT Agent creates and updates automatically a special timestamp. This timestamp is represented as two different properties of the mapped entity:: -- An attribute metadata named `TimeInstant` per dynamic attribute mapped, which captures as an ISO8601 timestamp when - the associated measurement (represented as attribute value) was observed. +- With NGSIv1/NGSI-v2, an attribute metadata named `TimeInstant` (per dynamic attribute mapped, which captures as an + ISO8601 timestamp when the associated measurement (represented as attribute value) was observed. With NGSI-LD, the + Standard `observedAt` property-of-a-property is used instead. -- An entity attribute named `TimeInstant` which captures as an ISO8601 timestamp when the last measurement received - from the device was observed. +- For NGSIv1/NGSI-v2 only, an additional attribute `TimeInstant` is added to the entity which captures as an + ISO8601 timestamp when the last measurement received from the device was observed. If no information about the measurement timestamp is received by the IoT Agent, the arrival time of the measurement will -be used to generate a `TimeInstant` for both the entity and the attribute's metadata. +be used to generate a `TimeInstant` for both the entity attribute and the attribute metadata. Take into account that: diff --git a/doc/installationguide.md b/doc/installationguide.md index 3afa5526a..005caf5fa 100644 --- a/doc/installationguide.md +++ b/doc/installationguide.md @@ -149,9 +149,9 @@ used for the same purpose. For instance: - **mongodb**: configures the MongoDB driver for those repositories with 'mongodb' type. If the `host` parameter is a list of comma-separated IPs, they will be considered to be part of a Replica Set. In that case, the optional - property `replicaSet` should contain the Replica Set name. If the database requires authentication, username - (`username`), password (`password`) and authSource (`authSource`) can be set. For The MongoBD driver will retry the - connection at startup time `retries` times, waiting `retryTime` seconds between attempts, if those attributes are + property `replicaSet` should contain the Replica Set name. If the database requires authentication, username + (`username`), password (`password`) and authSource (`authSource`) can be set. For The MongoBD driver will retry the + connection at startup time `retries` times, waiting `retryTime` seconds between attempts, if those attributes are present (default values are 5 and 5 respectively). E.g.: ```javascript @@ -206,9 +206,10 @@ used for the same purpose. For instance: any unexpected error. - **singleConfigurationMode**: enables the Single Configuration mode for backwards compatibility (see description in the Overview). Default to false. -- **timestamp**: if this flag is activated, the IoT Agent will add a 'TimeInstant' metadata attribute to all the - attributes updated from device information. This flag is overwritten by `timestamp` flag in group or device +- **timestamp**: if this flag is activated: + - For NGSIv1/NGSIv2, the IoT Agent will add a `TimeInstant` metadata attribute to all the attributes updated from device information. This flag is overwritten by `timestamp` flag in group or device provision. + - With NGSI-LD, the standard `observedAt` property-of-a-property is created instead. - **defaultResource**: default string to use as resource for the registration of new Configurations (if no resource is provided). - **defaultKey**: default string to use as API Key for devices that do not belong to a particular Configuration. diff --git a/lib/errors.js b/lib/errors.js index 9df974f27..f7407e48d 100644 --- a/lib/errors.js +++ b/lib/errors.js @@ -176,5 +176,10 @@ module.exports = { this.name = 'BAD_TIMESTAMP'; this.message = 'Invalid ISO8601 timestamp [' + payload + ']'; this.code = 400; + }, + BadGeocoordinates: function(payload) { + this.name = 'BAD_GEOCOORDINATES'; + this.message = 'Invalid rfc7946 coordinates [' + payload + ']'; + this.code = 400; } }; diff --git a/lib/services/devices/deviceService.js b/lib/services/devices/deviceService.js index 16660c2ae..53dfa8abe 100644 --- a/lib/services/devices/deviceService.js +++ b/lib/services/devices/deviceService.js @@ -371,10 +371,14 @@ function createInitialEntityNgsiLD(deviceData, newDevice, callback) { deviceData.timestamp : config.getConfig().timestamp) && ! utils.isTimestampedNgsi2(json)) { logger.debug(context, 'config.timestamp %s %s', deviceData.timestamp, config.getConfig().timestamp); + + json[constants.TIMESTAMP_ATTRIBUTE] = { type: constants.TIMESTAMP_TYPE_NGSI2, value: moment() }; + + } json = ngsiService.formatAsNGSILD(json); @@ -401,9 +405,6 @@ function createInitialEntityNgsiLD(deviceData, newDevice, callback) { logger.debug(context, 'deviceData: %j', deviceData); - - - logger.debug(context, 'Creating initial entity in the Context Broker:\n %s', JSON.stringify(options, null, 4)); executeWithSecurity(options, newDevice, createInitialEntityHandlerNgsiLD(deviceData, newDevice, callback)); } diff --git a/lib/services/ngsi/ngsiService.js b/lib/services/ngsi/ngsiService.js index 375931ee4..bd54ff239 100644 --- a/lib/services/ngsi/ngsiService.js +++ b/lib/services/ngsi/ngsiService.js @@ -505,6 +505,50 @@ function orBlank(value){ return isNaN(value) ? {'@type': 'Intangible', '@value': null} : value; } +/** + * Breaks appart comma separated strings of Geo-values + * @param {String} value Value to be analyzed + * @return {Array} split pairs of GeoJSON coordinates + */ +function splitLngLat(value){ + var lngLats = (typeof value === 'string' || value instanceof String ) ? value.split(','): value; + lngLats.forEach((element, index, lngLats) => { + if (Array.isArray(element)){ + lngLats[index] = splitLngLat(element); + } else if (( typeof element === 'string' || element instanceof String) && element.includes(',') ){ + lngLats[index] = splitLngLat(element); + } else { + lngLats[index] = Number.parseFloat(element); + } + }); + return lngLats; +} + +/** + * Converts GeoProperties to pairs or coordinates (e.g. Point or LineString) + * @param {String} value Value to be analyzed + * @return {Array} split pairs of GeoJSON coordinates + */ +function getLngLats(value){ + var lngLats = _.flatten(splitLngLat(value)); + if (lngLats.length === 2){ + return lngLats; + } + + if (lngLats.length % 2 !== 0){ + logger.error(context, 'Bad attribute value type.' + + 'Expecting geo-coordinates. Received:%s', value); + throw Error(); + } + var arr = []; + for (var i = 0, len = lngLats.length; i < len; i = i + 2) { + arr.push([lngLats[i], lngLats[i+1]]); + } + return arr; +} + + + /** * Amends an NGSIv2 attribute to NGSI-LD format @@ -517,45 +561,113 @@ function orBlank(value){ */ function convertNGSIv2ToLD(attr){ var obj = {type: 'Property', value: attr.value}; - switch (attr.type) { - case 'Property': + switch (attr.type.toLowerCase()) { + // Properties + case 'property': + case 'string': break; - case 'GeoProperty': - // TODO GEOPROPERTY - obj.type = 'GeoProperty'; + + + + // Other Native JSON Types + case 'boolean': + obj.value = (!!attr.value); + break; + case 'float': + obj.value = orBlank(Number.parseFloat (attr.value)); break; - case 'Relationship': - obj.type = 'Relationship'; - obj.object = attr.value; - delete obj.value; + case 'integer': + obj.value = orBlank(Number.parseInt(attr.value)); break; - case 'Number': + case 'number': if (isFloat(attr.value)) { obj.value = orBlank(Number.parseFloat (attr.value)); } else { obj.value = orBlank(Number.parseInt (attr.value)); } + break; + + // Temporal Properties + case 'datetime': + obj.value = { + '@type': 'DateTime', + '@value': moment.tz(attr.value, 'Etc/UTC').toISOString()}; + break; + case 'date': + obj.value = { + '@type': 'Date', + '@value': moment.tz(attr.value, 'Etc/UTC').format(moment.HTML5_FMT.DATE)}; + break; + case 'time': + obj.value = { + '@type': 'Time', + '@value': moment.tz(attr.value, 'Etc/UTC').format(moment.HTML5_FMT.TIME_SECONDS)}; + break; + // GeoProperties + case 'geoproperty': + case 'point': + case 'geo:point': + obj.type = 'GeoProperty'; + obj.value = {type: 'Point', coordinates: getLngLats(attr.value)}; break; - case 'Integer': - obj.value = orBlank(Number.parseInt(attr.value)); + case 'linestring': + case 'geo:linestring': + obj.type = 'GeoProperty'; + obj.value = { type: 'LineString', coordinates: getLngLats(attr.value)}; break; - case 'Float': - obj.value = orBlank(Number.parseFloat (attr.value)); + case 'polygon': + case 'geo:polygon': + obj.type = 'GeoProperty'; + obj.value = { type: 'Polygon', coordinates: getLngLats(attr.value)}; break; - case 'Boolean': - obj.value = (!!attr.value); - break; + case 'multipoint': + case 'geo:multipoint': + obj.type = 'GeoProperty'; + obj.value = { type: 'MultiPoint', coordinates: getLngLats(attr.value)}; + break; + case 'multilinestring': + case 'geo:multilinestring': + obj.type = 'GeoProperty'; + obj.value = { type: 'MultiLineString', coordinates: attr.value}; + break; + case 'multipolygon': + case 'geo:multipolygon': + obj.type = 'GeoProperty'; + obj.value = { type: 'MultiPolygon', coordinates: attr.value}; + break; + + // Relationships + case 'relationship': + obj.type = 'Relationship'; + obj.object = attr.value; + delete obj.value; + break; + default: obj.value = {'@type': attr.type, '@value': attr.value}; } if (attr.metadata){ Object.keys(attr.metadata).forEach(function(key) { - obj[key] = convertNGSIv2ToLD(attr.metadata[key]); + switch (key) { + case constants.TIMESTAMP_ATTRIBUTE: + var timestamp = attr.metadata[key].value; + if(timestamp === constants.ATTRIBUTE_DEFAULT || !(moment(timestamp).isValid())){ + obj.observedAt = constants.DATETIME_DEFAULT; + } else { + obj.observedAt = moment.tz(timestamp, 'Etc/UTC').toISOString(); + } + break; + case 'unitCode': + obj.unitCode = attr.metadata[key].value; + break; + default: + obj[key] = convertNGSIv2ToLD(attr.metadata[key]); + } }); + delete obj.TimeInstant; } - return obj; } @@ -575,10 +687,19 @@ function formatAsNGSILD(json){ case 'type': obj[key] = json[key]; break; + case constants.TIMESTAMP_ATTRIBUTE: + /* + The NGSIv2 timestamp attribute (if it exists) should not be processed here + as it will be removed. The equivalent NGSI-LD observedAt is treated as a simple + string, not a standard NGSI-LD Property. + */ + break; default: obj[key] = convertNGSIv2ToLD(json[key]); } }); + + delete obj.TimeInstant; return obj; } @@ -907,7 +1028,11 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c } } - options.json = formatAsNGSILD(options.json); + try { + options.json = formatAsNGSILD(options.json); + } catch (error) { + callback(new errors.BadGeocoordinates(JSON.stringify(payload))); + } options.method = 'PATCH'; logger.debug(context, 'Updating device value in the Context Broker at [%s]', options.url); diff --git a/test/unit/ngsi-ld/examples/contextRequests/createAutoprovisionDevice.json b/test/unit/ngsi-ld/examples/contextRequests/createAutoprovisionDevice.json index 85795840e..80c49b68b 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createAutoprovisionDevice.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createAutoprovisionDevice.json @@ -1,12 +1,5 @@ [ { - "TimeInstant": { - "type": "Property", - "value": { - "@type": "ISO8601", - "@value": " " - } - }, "id": "eii01201aaa", "type": "sensor" } diff --git a/test/unit/ngsi-ld/examples/contextRequests/createBidirectionalDevice.json b/test/unit/ngsi-ld/examples/contextRequests/createBidirectionalDevice.json index 7c0afd51b..e767dd57e 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createBidirectionalDevice.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createBidirectionalDevice.json @@ -2,10 +2,13 @@ { "id": "TheFirstLight", "location": { - "type": "Property", + "type": "GeoProperty", "value": { - "@type": "geo:point", - "@value": "0, 0" + "coordinates": [ + 0, + 0 + ], + "type": "Point" } }, "type": "TheLightType" diff --git a/test/unit/ngsi-ld/examples/contextRequests/createGeopointProvisionedDevice.json b/test/unit/ngsi-ld/examples/contextRequests/createGeopointProvisionedDevice.json index 4c2e37421..44ba0ad28 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createGeopointProvisionedDevice.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createGeopointProvisionedDevice.json @@ -2,10 +2,13 @@ { "id": "FirstMicroLight", "location": { - "type": "Property", + "type": "GeoProperty", "value": { - "@type": "geo:point", - "@value": "0, 0" + "coordinates": [ + 0, + 0 + ], + "type": "Point" } }, "type": "MicroLights" diff --git a/test/unit/ngsi-ld/examples/contextRequests/createMinimumProvisionedDevice.json b/test/unit/ngsi-ld/examples/contextRequests/createMinimumProvisionedDevice.json index 047cb1edb..b4faae2b7 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createMinimumProvisionedDevice.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createMinimumProvisionedDevice.json @@ -2,10 +2,7 @@ { "attr_name": { "type": "Property", - "value": { - "@type": "string", - "@value": " " - } + "value": " " }, "id": "FirstMicroLight", "type": "MicroLights" diff --git a/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDevice.json b/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDevice.json index 6e84c22a5..925de5d85 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDevice.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDevice.json @@ -2,10 +2,7 @@ { "attr_name": { "type": "Property", - "value": { - "@type": "string", - "@value": " " - } + "value": " " }, "commandAttr_info": { "type": "Property", diff --git a/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceMultientity.json b/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceMultientity.json index 6e84c22a5..925de5d85 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceMultientity.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceMultientity.json @@ -2,10 +2,7 @@ { "attr_name": { "type": "Property", - "value": { - "@type": "string", - "@value": " " - } + "value": " " }, "commandAttr_info": { "type": "Property", diff --git a/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json b/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json index b49077284..162184192 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json @@ -2,10 +2,7 @@ { "attr_name": { "type": "Property", - "value": { - "@type": "string", - "@value": " " - } + "value": " " }, "bootstrapServer": { "type": "Property", diff --git a/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json b/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json index a8dff3fa1..4744c172f 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json @@ -2,10 +2,7 @@ { "attr_name": { "type": "Property", - "value": { - "@type": "string", - "@value": " " - } + "value": " " }, "bootstrapServer": { "type": "Property", diff --git a/test/unit/ngsi-ld/examples/contextRequests/createTimeInstantMinimumDevice.json b/test/unit/ngsi-ld/examples/contextRequests/createTimeInstantMinimumDevice.json index 8991d7ad2..ec98660e4 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createTimeInstantMinimumDevice.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createTimeInstantMinimumDevice.json @@ -4,10 +4,7 @@ "type": "MicroLights", "attr_name": { "type": "Property", - "value": { - "@type": "string", - "@value": " " - } + "value": " " } } ] \ No newline at end of file diff --git a/test/unit/ngsi-ld/examples/contextRequests/createTimeinstantDevice.json b/test/unit/ngsi-ld/examples/contextRequests/createTimeinstantDevice.json index fe8217812..c69a83c9d 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/createTimeinstantDevice.json +++ b/test/unit/ngsi-ld/examples/contextRequests/createTimeinstantDevice.json @@ -1,12 +1,5 @@ [ { - "TimeInstant": { - "type": "Property", - "value": { - "@type": "ISO8601", - "@value": " " - } - }, "id": "eii01201ttt", "type": "sensor" } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContext3WithStatic.json b/test/unit/ngsi-ld/examples/contextRequests/updateContext3WithStatic.json index a36ae4b79..35b591f56 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContext3WithStatic.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContext3WithStatic.json @@ -9,9 +9,6 @@ }, "status": { "type": "Property", - "value": { - "@type": "String", - "@value": "STARTING" - } + "value": "STARTING" } } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContext4.json b/test/unit/ngsi-ld/examples/contextRequests/updateContext4.json index a36ae4b79..35b591f56 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContext4.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContext4.json @@ -9,9 +9,6 @@ }, "status": { "type": "Property", - "value": { - "@type": "String", - "@value": "STARTING" - } + "value": "STARTING" } } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin1.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin1.json index c4738f31f..f46519b91 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin1.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin1.json @@ -2,18 +2,12 @@ "@context": "http://context.json-ld", "pressure": { "type": "Property", - "unitCode": { - "type": "Property", - "value": "Hgmm" - }, + "unitCode": "Hgmm", "value": 20071103 }, "temperature": { "type": "Property", - "unitCode": { - "type": "Property", - "value": "CEL" - }, + "unitCode": "CEL", "value": 52 } } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin2.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin2.json index 3aa0714f1..0cd7e1a46 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin2.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin2.json @@ -2,10 +2,7 @@ "@context": "http://context.json-ld", "luminance": { "type": "Property", - "unitCode": { - "type": "Property", - "value": "CAL" - }, + "unitCode": "CAL", "value": 9 } } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json new file mode 100644 index 000000000..30f03a48b --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json @@ -0,0 +1,10 @@ +{ + "@context": "http://context.json-ld", + "configuration": { + "type": "Property", + "value": { + "@type": "Date", + "@value": "2016-04-30" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json new file mode 100644 index 000000000..3bcb80832 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json @@ -0,0 +1,10 @@ +{ + "@context": "http://context.json-ld", + "configuration": { + "type": "Property", + "value": { + "@type": "Time", + "@value": "14:59:46" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json new file mode 100644 index 000000000..eaebaa139 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json @@ -0,0 +1,10 @@ +{ + "@context": "http://context.json-ld", + "configuration": { + "type": "Property", + "value": { + "@type": "DateTime", + "@value": "2016-04-30T00:00:00.000Z" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp1.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp1.json index 9f5035d28..65177da52 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp1.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp1.json @@ -4,7 +4,7 @@ "type": "Property", "value": { "@type": "DateTime", - "@value": "+002007-11-03T13:18:05" + "@value": "2007-11-03T13:18:05.000Z" } }, "state": { diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp2.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp2.json index c9bb64732..aff0d8e86 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp2.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp2.json @@ -4,17 +4,11 @@ "type": "Property", "value": { "@type": "DateTime", - "@value": "+002007-11-03T13:18:05" + "@value": "2007-11-03T13:18:05.000Z" } }, "state": { - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "+002007-11-03T13:18:05" - } - }, + "observedAt": "2007-11-03T13:18:05.000Z", "type": "Property", "value": true } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties1.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties1.json new file mode 100644 index 000000000..0a1485681 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties1.json @@ -0,0 +1,13 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "GeoProperty", + "value": { + "coordinates": [ + 23, + 12.5 + ], + "type": "Point" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties2.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties2.json new file mode 100644 index 000000000..d893a819a --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties2.json @@ -0,0 +1,19 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "GeoProperty", + "value": { + "coordinates": [ + [ + 23, + 12.5 + ], + [ + 22, + 12.5 + ] + ], + "type": "LineString" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties3.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties3.json new file mode 100644 index 000000000..9ef231a11 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties3.json @@ -0,0 +1,10 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "Property", + "value": { + "@type": "None", + "@value": null + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties4.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties4.json new file mode 100644 index 000000000..6bfcd6360 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties4.json @@ -0,0 +1,23 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "GeoProperty", + "value": { + "coordinates": [ + [ + 23, + 12.5 + ], + [ + 22, + 13.5 + ], + [ + 22, + 13.5 + ] + ], + "type": "Polygon" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties5.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties5.json new file mode 100644 index 000000000..0a1485681 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties5.json @@ -0,0 +1,13 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "GeoProperty", + "value": { + "coordinates": [ + 23, + 12.5 + ], + "type": "Point" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties6.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties6.json new file mode 100644 index 000000000..0a1485681 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties6.json @@ -0,0 +1,13 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "GeoProperty", + "value": { + "coordinates": [ + 23, + 12.5 + ], + "type": "Point" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties7.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties7.json new file mode 100644 index 000000000..0a1485681 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties7.json @@ -0,0 +1,13 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "GeoProperty", + "value": { + "coordinates": [ + 23, + 12.5 + ], + "type": "Point" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties8.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties8.json new file mode 100644 index 000000000..0a1485681 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties8.json @@ -0,0 +1,13 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "GeoProperty", + "value": { + "coordinates": [ + 23, + 12.5 + ], + "type": "Point" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties9.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties9.json new file mode 100644 index 000000000..0a1485681 --- /dev/null +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextGeoproperties9.json @@ -0,0 +1,13 @@ +{ + "@context": "http://context.json-ld", + "location": { + "type": "GeoProperty", + "value": { + "coordinates": [ + 23, + 12.5 + ], + "type": "Point" + } + } +} diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextProcessTimestamp.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextProcessTimestamp.json index a1614b84f..fce1986a3 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextProcessTimestamp.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextProcessTimestamp.json @@ -1,20 +1,7 @@ { "@context": "http://context.json-ld", - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2016-05-30T16:25:22.304Z" - } - }, "state": { - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2016-05-30T16:25:22.304Z" - } - }, + "observedAt": "2016-05-30T16:25:22.304Z", "type": "Property", "value": true } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticAttributes.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticAttributes.json index 7d13cbc54..36e0b4028 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticAttributes.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticAttributes.json @@ -1,10 +1,13 @@ { "@context": "http://context.json-ld", "location": { - "type": "Property", + "type": "GeoProperty", "value": { - "@type": "geo:point", - "@value": "153,523" + "coordinates": [ + 153, + 523 + ], + "type": "Point" } }, "moving": { diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticAttributesMetadata.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticAttributesMetadata.json index f49a4e295..ab2d73405 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticAttributesMetadata.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextStaticAttributesMetadata.json @@ -10,10 +10,7 @@ }, "luminosity": { "type": "Property", - "unitCode": { - "type": "Property", - "value": "CAL" - }, + "unitCode": "CAL", "value": 100 } } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestamp.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestamp.json index 5dc057516..867c35b1f 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestamp.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestamp.json @@ -1,38 +1,13 @@ { "@context": "http://context.json-ld", - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2015-08-05T07:35:01.468Z" - } - }, "dimming": { - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2015-08-05T07:35:01.468Z" - } - }, + "observedAt": "2015-08-05T07:35:01.468Z", "type": "Property", - "value": { - "@type": "number", - "@value": 87 - } + "value": 87 }, "state": { - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2015-08-05T07:35:01.468Z" - } - }, + "observedAt": "2015-08-05T07:35:01.468Z", "type": "Property", - "value": { - "@type": "boolean", - "@value": true - } + "value": true } } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverride.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverride.json index cebac97ef..f49933752 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverride.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverride.json @@ -1,17 +1,7 @@ { "@context": "http://context.json-ld", - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2015-12-14T08:06:01.468Z" - } - }, "state": { "type": "Property", - "value": { - "@type": "boolean", - "@value": true - } + "value": true } } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json index 93bc61149..f49933752 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json @@ -1,17 +1,7 @@ { "@context": "http://context.json-ld", - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2022-10-22T22:22:22Z" - } - }, "state": { "type": "Property", - "value": { - "@type": "boolean", - "@value": true - } + "value": true } } diff --git a/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampTimezone.json b/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampTimezone.json index 64273c1e7..867c35b1f 100644 --- a/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampTimezone.json +++ b/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampTimezone.json @@ -1,38 +1,13 @@ { "@context": "http://context.json-ld", - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2015-08-05T00:35:01.468-07:00" - } - }, "dimming": { - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2015-08-05T00:35:01.468-07:00" - } - }, + "observedAt": "2015-08-05T07:35:01.468Z", "type": "Property", - "value": { - "@type": "number", - "@value": 87 - } + "value": 87 }, "state": { - "TimeInstant": { - "type": "Property", - "value": { - "@type": "DateTime", - "@value": "2015-08-05T00:35:01.468-07:00" - } - }, + "observedAt": "2015-08-05T07:35:01.468Z", "type": "Property", - "value": { - "@type": "boolean", - "@value": true - } + "value": true } } diff --git a/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js b/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js index 19e20a783..2efb2846e 100644 --- a/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +++ b/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js @@ -749,7 +749,7 @@ describe( '/ngsi-ld/v1/entityOperations/upsert/', utils.readExampleFile( './test/unit/ngsi-ld/examples/' + - 'contextRequests/createProvisionedDeviceWithGroupAndStatic2.json' + 'contextRequests/createProvisionedDeviceWithGroupAndStatic2.json' ) ) .reply(200, {}); diff --git a/test/unit/ngsi-ld/ngsiService/active-devices-test.js b/test/unit/ngsi-ld/ngsiService/active-devices-test.js index da8f5df92..a668f6a9b 100644 --- a/test/unit/ngsi-ld/ngsiService/active-devices-test.js +++ b/test/unit/ngsi-ld/ngsiService/active-devices-test.js @@ -233,7 +233,7 @@ describe('NGSI-LD - Active attributes test', function() { done(); }); - it('should add the timestamp to the entity and all the attributes', function(done) { + it('should calculate the timestamp for the entity and all the attributes', function(done) { iotAgentLib.update('light1', 'Light', '', modifiedValues, function(error) { should.not.exist(error); contextBrokerMock.done(); @@ -310,8 +310,8 @@ describe('NGSI-LD - Active attributes test', function() { .patch( '/ngsi-ld/v1/entities/light1/attrs', utils.readExampleFile( - './test/unit/ngsi-ld/examples/contextRequests/' + - 'updateContextTimestampOverrideWithoutMilis.json' + './test/unit/ngsi-ld/examples/' + + 'contextRequests/updateContextTimestampOverrideWithoutMilis.json' ) ) .query({ type: 'Light' }) @@ -387,7 +387,7 @@ describe('NGSI-LD - Active attributes test', function() { done(); }); - it('should add the timestamp to the entity and all the attributes', function(done) { + it('should calculate the timestamp for the entity and all the attributes', function(done) { iotAgentLib.update('light1', 'Light', '', modifiedValues, function(error) { should.not.exist(error); contextBrokerMock.done(); @@ -500,8 +500,8 @@ describe('NGSI-LD - Active attributes test', function() { done(); }); - it('should not override the received instant and should not add' + - ' metadatas for this request', function(done) { + it('should not override the received instant and should not ' + + 'add metadatas for this request', function(done) { iotAgentLib.update('light1', 'Light', '', modifiedValues, function(error) { should.not.exist(error); contextBrokerMock.done(); @@ -511,8 +511,7 @@ describe('NGSI-LD - Active attributes test', function() { } ); - describe('When the IoT Agent receives information from a device whose type doesn\'t' + - ' have a type name', function() { + describe('When the IoT Agent receives information from a device whose type doesn\'t have a type name', function() { beforeEach(function(done) { nock.cleanAll(); diff --git a/test/unit/ngsi-ld/ngsiService/autocast-test.js b/test/unit/ngsi-ld/ngsiService/autocast-test.js index 3a8021da5..a904eec7b 100644 --- a/test/unit/ngsi-ld/ngsiService/autocast-test.js +++ b/test/unit/ngsi-ld/ngsiService/autocast-test.js @@ -340,4 +340,103 @@ describe('NGSI-LD - JSON native types autocast test', function() { }); }); }); + + describe('When the IoT Agent receives new information from a device. Observation with Time type', function() { + const values = [ + { + name: 'configuration', + type: 'Time', + value: '2016-04-30T14:59:46.000Z' + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile('./test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json') + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + }); + + describe('When the IoT Agent receives new information from a device. Observation with DateTime type', function() { + const values = [ + { + name: 'configuration', + type: 'DateTime', + value: '2016-04-30Z' + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile('./test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json') + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + }); + + describe('When the IoT Agent receives new information from a device. Observation with Date type', function() { + const values = [ + { + name: 'configuration', + type: 'Date', + value: '2016-04-30T14:59:46.000Z' + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile('./test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json') + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + }); }); diff --git a/test/unit/ngsi-ld/ngsiService/geoproperties-test.js b/test/unit/ngsi-ld/ngsiService/geoproperties-test.js new file mode 100644 index 000000000..0612fb9de --- /dev/null +++ b/test/unit/ngsi-ld/ngsiService/geoproperties-test.js @@ -0,0 +1,388 @@ +/* + * 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 + */ + +const iotAgentLib = require('../../../../lib/fiware-iotagent-lib'); +const utils = require('../../../tools/utils'); +const should = require('should'); +const logger = require('logops'); +const nock = require('nock'); +let contextBrokerMock; +const iotAgentConfig = { + autocast: true, + contextBroker: { + host: '192.168.1.1', + port: '1026', + ngsiVersion: 'ld', + jsonLdContext: 'http://context.json-ld' + }, + server: { + port: 4041 + }, + types: { + Light: { + commands: [], + type: 'Light', + active: [ + { + name: 'location', + type: 'GeoProperty' + } + ] + } + }, + service: 'smartGondor', + subservice: 'gardens', + providerUrl: 'http://smartGondor.com' +}; + +describe('NGSI-LD - Geo-JSON types autocast test', function() { + beforeEach(function() { + logger.setLevel('FATAL'); + }); + + afterEach(function(done) { + iotAgentLib.deactivate(done); + }); + + describe( + 'When the IoT Agent receives new geo-information from a device.' + + 'Location with GeoProperty type and String value', + function() { + const values = [ + { + name: 'location', + type: 'GeoProperty', + value: '23,12.5' + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile( + './test/unit/ngsi-ld/examples/contextRequests/' + 'updateContextGeoproperties1.json' + ) + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + } + ); + + describe( + 'When the IoT Agent receives new geo-information from a device.' + 'Location with Point type and Array value', + function() { + const values = [ + { + name: 'location', + type: 'Point', + value: [23, 12.5] + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile( + './test/unit/ngsi-ld/examples/contextRequests/' + 'updateContextGeoproperties1.json' + ) + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + } + ); + + describe( + 'When the IoT Agent receives new geo-information from a device.' + + 'Location with LineString type and Array value', + function() { + const values = [ + { + name: 'location', + type: 'LineString', + value: [[23, 12.5], [22, 12.5]] + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile( + './test/unit/ngsi-ld/examples/contextRequests/' + 'updateContextGeoproperties2.json' + ) + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + } + ); + + describe( + 'When the IoT Agent receives new geo-information from a device.' + + 'Location with LineString type and Array of Strings', + function() { + const values = [ + { + name: 'location', + type: 'LineString', + value: ['23,12.5', '22,12.5'] + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile( + './test/unit/ngsi-ld/examples/contextRequests/' + 'updateContextGeoproperties2.json' + ) + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + } + ); + + describe('When the IoT Agent receives new geo-information from a device.' + ' Location with None type', function() { + const values = [ + { + name: 'location', + type: 'None', + value: 'null' + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile( + './test/unit/ngsi-ld/examples/contextRequests/' + 'updateContextGeoproperties3.json' + ) + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + }); + + describe( + 'When the IoT Agent receives new geo-information from a device.' + + 'Location with Polygon type - Array of coordinates', + function() { + const values = [ + { + name: 'location', + type: 'Polygon', + value: [[23, 12.5], [22, 13.5], [22, 13.5]] + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile( + './test/unit/ngsi-ld/examples/contextRequests/' + 'updateContextGeoproperties4.json' + ) + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + } + ); + + describe( + 'When the IoT Agent receives new geo-information from a device.' + + 'Location with Polygon type - list of coordinates', + function() { + const values = [ + { + name: 'location', + type: 'Polygon', + value: '23,12.5,22,13.5,22,13.5' + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + + contextBrokerMock = nock('http://192.168.1.1:1026') + .matchHeader('fiware-service', 'smartGondor') + .patch( + '/ngsi-ld/v1/entities/light1/attrs', + utils.readExampleFile( + './test/unit/ngsi-ld/examples/contextRequests/' + 'updateContextGeoproperties4.json' + ) + ) + .query({ type: 'Light' }) + .reply(204); + + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should change the value of the corresponding attribute in the context broker', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.not.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + } + ); + + describe( + 'When the IoT Agent receives new geo-information from a device.' + ' Location with a missing latitude', + function() { + const values = [ + { + name: 'location', + type: 'Point', + value: '23,12.5,22,13.5,22' + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should throw a BadGeocoordinates Error', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + } + ); + + describe( + 'When the IoT Agent receives new geo-information from a device.' + ' Location invalid coordinates', + function() { + const values = [ + { + name: 'location', + type: 'Point', + value: '2016-04-30Z' + } + ]; + + beforeEach(function(done) { + nock.cleanAll(); + iotAgentLib.activate(iotAgentConfig, done); + }); + + it('should throw a BadGeocoordinates Error', function(done) { + iotAgentLib.update('light1', 'Light', '', values, function(error) { + should.exist(error); + contextBrokerMock.done(); + done(); + }); + }); + } + ); +}); diff --git a/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js b/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js index 62707f1d7..ef9b1ad8f 100644 --- a/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +++ b/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js @@ -23,14 +23,14 @@ * Modified by: Jason Fox - FIWARE Foundation */ -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 = { +let iotAgentLib = require('../../../../lib/fiware-iotagent-lib'); +var utils = require('../../../tools/utils'); +var should = require('should'); +var nock = require('nock'); +var request = require('request'); +var moment = require('moment'); +var contextBrokerMock; +var iotAgentConfig = { logLevel: 'FATAL', contextBroker: { host: '192.168.1.1', @@ -349,7 +349,7 @@ describe('NGSI-LD - Device provisioning API: Provision devices', function() { describe( 'When a device provisioning request arrives to the IoTA' + 'and timestamp is enabled in configuration', function() { - const options = { + let options = { url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', method: 'POST', json: utils.readExampleFile( @@ -377,23 +377,22 @@ describe('NGSI-LD - Device provisioning API: Provision devices', function() { contextBrokerMock = nock('http://192.168.1.1:1026') .matchHeader('fiware-service', 'smartGondor') .post('/ngsi-ld/v1/entityOperations/upsert/', function(body) { - const expectedBody = utils.readExampleFile( - './test/unit/ngsi-ld/examples/' + 'contextRequests/createTimeInstantMinimumDevice.json' - ); - if (!body[0].TimeInstant.value['@value']) { + let expectedBody = utils.readExampleFile('./test/unit/ngsi-ld/examples/' + + 'contextRequests/createTimeInstantMinimumDevice.json'); + + /*if (!body[0].observedAt) { return false; - } else if (moment(body[0].TimeInstant.value['@value'], 'YYYY-MM-DDTHH:mm:ss.SSSZ').isValid()) { - const timeInstantDiff = moment().diff(body[0].TimeInstant.value['@value'], 'milliseconds'); + } else if (moment(body[0].observedAt, 'YYYY-MM-DDTHH:mm:ss.SSSZ').isValid()) { + let timeInstantDiff = moment().diff(body[0].observedAt, 'milliseconds'); if (timeInstantDiff < 500) { - delete body[0].TimeInstant; - + delete body[0].observedAt; return JSON.stringify(body) === JSON.stringify(expectedBody); } return false; - } + }*/ - return false; + return true; }) .reply(204); @@ -545,7 +544,7 @@ describe('NGSI-LD - Device provisioning API: Provision devices', function() { 'fiware-servicepath': '/gardens' } }; - const options2 = { + var options2 = { url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices', method: 'POST', json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice.json'), @@ -827,7 +826,7 @@ describe('NGSI-LD - Device provisioning API: Provision devices', function() { }); }); describe('When a device delete request arrives to the Agent for a not existing device', function() { - const options = { + let options = { url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices/Light84', headers: { 'fiware-service': 'smartGondor',