iotagent-node-lib 3.1.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +16 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.yml +55 -0
  4. package/.github/advanced-issue-labeler.yml +30 -0
  5. package/.github/workflows/issue-labeler.yml +43 -0
  6. package/CHANGES_NEXT_RELEASE +0 -2
  7. package/config.js +5 -5
  8. package/doc/api.md +1483 -298
  9. package/doc/deprecated.md +7 -1
  10. package/doc/development.md +120 -0
  11. package/doc/howto.md +58 -62
  12. package/doc/installationguide.md +3 -11
  13. package/doc/requirements.txt +1 -1
  14. package/docker/Mosquitto/Dockerfile +1 -1
  15. package/lib/commonConfig.js +7 -10
  16. package/lib/fiware-iotagent-lib.js +0 -10
  17. package/lib/jexlTranformsMap.js +2 -1
  18. package/lib/model/Device.js +0 -1
  19. package/lib/model/Group.js +0 -1
  20. package/lib/model/dbConn.js +1 -7
  21. package/lib/plugins/bidirectionalData.js +8 -26
  22. package/lib/plugins/expressionPlugin.js +8 -40
  23. package/lib/plugins/jexlParser.js +28 -0
  24. package/lib/services/commands/commandService.js +1 -1
  25. package/lib/services/common/iotManagerService.js +0 -1
  26. package/lib/services/devices/deviceRegistryMongoDB.js +10 -10
  27. package/lib/services/devices/deviceService.js +16 -20
  28. package/lib/services/devices/devices-NGSI-v2.js +2 -5
  29. package/lib/services/ngsi/entities-NGSI-LD.js +16 -60
  30. package/lib/services/ngsi/entities-NGSI-v2.js +179 -119
  31. package/lib/services/northBound/deviceProvisioningServer.js +17 -17
  32. package/lib/templates/createDevice.json +5 -6
  33. package/lib/templates/createDeviceLax.json +6 -8
  34. package/lib/templates/deviceGroup.json +1 -5
  35. package/lib/templates/updateDevice.json +9 -2
  36. package/lib/templates/updateDeviceLax.json +14 -5
  37. package/package.json +1 -1
  38. package/scripts/legacy_expression_tool/README.md +262 -0
  39. package/scripts/legacy_expression_tool/legacy_expression_tool.py +423 -0
  40. package/scripts/legacy_expression_tool/requirements.txt +3 -0
  41. package/test/unit/examples/deviceProvisioningRequests/provisionBidirectionalDevice.json +5 -5
  42. package/test/unit/examples/groupProvisioningRequests/bidirectionalGroup.json +4 -4
  43. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +3 -13
  44. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +0 -2
  45. package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +31 -30
  46. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +8 -8
  47. package/test/unit/ngsi-ld/plugins/custom-plugin_test.js +152 -0
  48. package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +1 -1
  49. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +33 -37
  50. package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +5 -1
  51. package/test/unit/ngsiv2/examples/contextRequests/updateContext.json +2 -0
  52. package/test/unit/ngsiv2/examples/contextRequests/updateContext1.json +3 -1
  53. package/test/unit/ngsiv2/examples/contextRequests/updateContext3WithStatic.json +2 -0
  54. package/test/unit/ngsiv2/examples/contextRequests/updateContext4.json +4 -1
  55. package/test/unit/ngsiv2/examples/contextRequests/updateContext5.json +12 -0
  56. package/test/unit/ngsiv2/examples/contextRequests/updateContext6.json +10 -0
  57. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin1.json +2 -0
  58. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin2.json +3 -1
  59. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin3.json +3 -1
  60. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin4.json +3 -1
  61. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin5.json +3 -1
  62. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin6.json +3 -1
  63. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin7.json +3 -1
  64. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin8.json +3 -1
  65. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin9.json +3 -1
  66. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast1.json +2 -0
  67. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast2.json +2 -0
  68. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast3.json +3 -1
  69. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast4.json +3 -1
  70. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast5.json +3 -1
  71. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast6.json +3 -1
  72. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast7.json +3 -1
  73. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandError.json +3 -1
  74. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandExpired.json +3 -1
  75. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandFinish.json +3 -1
  76. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus.json +2 -0
  77. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +2 -0
  78. package/test/unit/ngsiv2/examples/contextRequests/updateContextCompressTimestamp1.json +3 -1
  79. package/test/unit/ngsiv2/examples/contextRequests/updateContextCompressTimestamp2.json +3 -1
  80. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin1.json +2 -0
  81. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin11.json +2 -0
  82. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin12.json +2 -0
  83. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin13.json +3 -1
  84. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin2.json +2 -0
  85. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin29.json +2 -0
  86. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin3.json +2 -0
  87. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin30.json +2 -0
  88. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin31.json +2 -8
  89. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +2 -0
  90. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin33.json +2 -0
  91. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +2 -0
  92. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +22 -0
  93. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
  94. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin4.json +2 -0
  95. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin40.json +42 -0
  96. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +33 -0
  97. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin5.json +2 -0
  98. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin6.json +2 -0
  99. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin7.json +2 -0
  100. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin8.json +2 -0
  101. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin9.json +2 -0
  102. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionSkip.json +12 -0
  103. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityJexlExpressionPlugin1.json +1 -1
  104. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin1.json +1 -1
  105. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin10.json +1 -1
  106. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin11.json +1 -1
  107. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin12.json +1 -1
  108. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin13.json +1 -1
  109. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin14.json +1 -1
  110. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +1 -1
  111. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +1 -1
  112. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +1 -1
  113. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin2.json +1 -1
  114. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +37 -0
  115. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin3.json +1 -1
  116. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin4.json +1 -1
  117. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin5.json +1 -1
  118. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin6.json +1 -1
  119. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin7.json +1 -1
  120. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin8.json +1 -1
  121. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin9.json +1 -1
  122. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +1 -1
  123. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +1 -1
  124. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -1
  125. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +2 -0
  126. package/test/unit/ngsiv2/examples/contextRequests/updateContextProcessTimestamp.json +2 -0
  127. package/test/unit/ngsiv2/examples/contextRequests/updateContextStaticAttributes.json +2 -0
  128. package/test/unit/ngsiv2/examples/contextRequests/updateContextStaticAttributesMetadata.json +3 -1
  129. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestamp.json +3 -1
  130. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampFalse.json +12 -0
  131. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampFalseTimeInstant.json +12 -0
  132. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverride.json +2 -0
  133. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +2 -0
  134. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampTimezone.json +3 -1
  135. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +355 -75
  136. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +18 -51
  137. package/test/unit/ngsiv2/general/https-support-test.js +1 -5
  138. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +4 -10
  139. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +9 -26
  140. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +143 -57
  141. package/test/unit/ngsiv2/ngsiService/autocast-test.js +14 -21
  142. package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +3 -5
  143. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +1 -10
  144. package/test/unit/ngsiv2/plugins/alias-plugin_test.js +20 -30
  145. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +6 -69
  146. package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +4 -6
  147. package/test/unit/ngsiv2/plugins/custom-plugin_test.js +150 -0
  148. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +87 -16
  149. package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +2 -3
  150. package/test/unit/ngsiv2/provisioning/device-group-api-test.js +2 -3
  151. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +15 -55
  152. package/test/unit/ngsiv2/provisioning/device-registration_test.js +1 -7
  153. package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +2 -9
  154. package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +0 -11
  155. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +0 -7
  156. package/test/unit/plugins/capture-provision-inPlugins_test.js +0 -6
  157. package/doc/advanced-topics.md +0 -626
  158. package/doc/expressionLanguage.md +0 -762
  159. package/lib/plugins/expressionParser.js +0 -205
  160. package/test/unit/expressions/expression-test.js +0 -197
  161. package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +0 -882
  162. package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -951
  163. package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -296
package/doc/deprecated.md CHANGED
@@ -21,6 +21,9 @@ A list of deprecated features and the version in which they were deprecated foll
21
21
  - Support to NGSI-LD v1.3 in iotagent-node-lib 2.25.0 (finally removed in 2.26.0)
22
22
  - Support groups (provision) statically defined by configuration
23
23
  - Support to in-memory registry (i.e.`deviceRegistry.type=memory`)
24
+ - Support to legacy expressions (finally removed in 3.2.0)
25
+ - Bidirectinal pluging (to be removed in 3.4.0)
26
+ - appendMode (to be removed in 3.4.0)
24
27
 
25
28
  The use of Node.js v14 is highly recommended.
26
29
 
@@ -48,4 +51,7 @@ The following table provides information about the last iotagent-node-lib versio
48
51
  | Support to Node.js v8 | 2.12.0 | April 7th, 2020 |
49
52
  | Support to Node.js v10 | 2.15.0 | February 18th, 2021 |
50
53
  | Support to Node.js v12 | 2.24.0 | September 2nd, 2022 |
51
- | Support to NGSI-LD 1.3 | 2.25.0 | January 24th, 2023 |
54
+ | Support to NGSI-LD 1.3 | 2.25.0 | January 24th, 2023 |
55
+ | Support to Legacy Expressions | 3.1.0 | April 25th, 2023 |
56
+ | bidirrectional plugin | 3.3.0 | August 24th, 2023 |
57
+ | appendMode | 3.3.0 | August 24th, 2023 |
@@ -7,6 +7,9 @@
7
7
  - [Continuous testing](#continuous-testing)
8
8
  - [Code Coverage](#code-coverage)
9
9
  - [Clean](#clean)
10
+ - [Data mapping plugins](#data-mapping-plugins)
11
+ - [Development](#development)
12
+ - [Provided plugins](#provided-plugins)
10
13
 
11
14
  ### Contributions
12
15
 
@@ -163,3 +166,120 @@ To ensure consistent Markdown formatting run the following:
163
166
  # Use git-bash on Windows
164
167
  npm run prettier:text
165
168
  ```
169
+
170
+ # DB Models (from API document)
171
+
172
+ ## Service group model
173
+
174
+ The table below shows the information held in the service group provisioning resource. The table also contains the
175
+ correspondence between the API resource fields and the same fields in the database model.
176
+
177
+ | Payload Field | DB Field | Definition |
178
+ | ------------------------------ | ------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---- |
179
+ | `service` | `service` | Service of the devices of this type |
180
+ | `subservice` | `subservice` | Subservice of the devices of this type. |
181
+ | `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). |
182
+ | `apikey` | `apikey` | API Key string. |
183
+ | `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 |
184
+ | `entity_type` | `entity_type` | name of the Entity `type` to assign to the group. |
185
+ | `trust` | `trust` | trust token to use for secured access to the Context Broker for this type of devices (optional; only needed for secured scenarios). |
186
+ | `cbHost` | `cbHost` | Context Broker connection information. This options can be used to override the global ones for specific types of devices. |
187
+ | `lazy` | `lazy` | list of common lazy attributes of the device. For each attribute, its `name` and `type` must be provided. |
188
+ | `commands` | `commands` | list of common commands attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. |
189
+ | `attributes` | `attributes` | list of common active attributes of the device. For each attribute, its `name` and `type` must be provided, additional `metadata` is optional. |
190
+ | `static_attributes` | `staticAttributes` | this attributes will be added to all the entities of this group 'as is', additional `metadata` is optional. |
191
+ | `internal_attributes` | `internalAttributes` | optional section with free format, to allow specific IoT Agents to store information along with the devices in the Device Registry. |
192
+ | `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. |
193
+ | `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) |
194
+ | `entityNameExp` | `entityNameExp` | optional field to allow use expressions to define entity name, instead default `id` and `type` |
195
+ | `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. |
196
+ | `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. |
197
+ | `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`. |
198
+
199
+ ## Device model
200
+
201
+ The table below shows the information held in the Device resource. The table also contains the correspondence between
202
+ the API resource fields and the same fields in the database model.
203
+
204
+ | Payload Field | DB Field | Definition | Example of value |
205
+ | --------------------- | -------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------- |
206
+ | `device_id` | `id` | Device ID that will be used to identify the device. | UO834IO |
207
+ | `service` | `service` | Name of the service the device belongs to (will be used in the fiware-service header). | smartGondor |
208
+ | `service_path` | `subservice` | Name of the subservice the device belongs to (used in the fiware-servicepath header). | /gardens |
209
+ | `entity_name` | `name` | Name of the entity representing the device in the Context Broker | ParkLamplight12 |
210
+ | `entity_type` | `type` | Type of the entity in the Context Broker | Lamplights |
211
+ | `timezone` | `timezone` | Time zone of the sensor if it has any | America/Santiago |
212
+ | `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 |
213
+ | `apikey` | `apikey` | Optional Apikey key string to use instead of group apikey | 9n4hb1vpwbjozzmw9f0flf9c2 |
214
+ | `endpoint` | `endpoint` | Endpoint where the device is going to receive commands, if any. | http://theDeviceUrl:1234/commands |
215
+ | `protocol` | `protocol` | Name of the device protocol, for its use with an IoT Manager. | IoTA-UL |
216
+ | `transport` | `transport` | Name of the device transport protocol, for the IoT Agents with multiple transport protocols. | MQTT |
217
+ | `attributes` | `active` | List of active attributes of the device | `[ { "name": "attr_name", "type": "Text" } ]` |
218
+ | `lazy` | `lazy` | List of lazy attributes of the device | `[ { "name": "attr_name", "type": "Text" } ]` |
219
+ | `commands` | `commands` | List of commands of the device | `[ { "name": "attr_name", "type": "Text" } ]` |
220
+ | `internal_attributes` | `internalAttributes` | List of internal attributes with free format for specific IoT Agent configuration | LWM2M mappings from object URIs to attributes |
221
+ | `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" } ]` |
222
+ | `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. |
223
+ | `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) |
224
+ | `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` |
225
+
226
+ ## Data mapping plugins
227
+
228
+ The IoT Agent Library provides a plugin mechanism in order to facilitate reusing code that makes small transformations
229
+ on incoming data (both from the device and from the context consumers). This mechanism is based in the use of
230
+ middlewares, i.e.: small pieces of code that receive and return an `entity`, making as many changes as they need, but
231
+ taking care of returning a valid entity, that can be used as the input for other middlewares; this way, all those pieces
232
+ of code can be chained together in order to make all the needed transformations in the target entity.
233
+
234
+ There are two kinds of middlewares: updateContext middlewares and queryContext middlewares. The updateContext
235
+ middlewares are applied before the information is sent to the Context Broker, modifiying the entity before it is sent to
236
+ Orion. The queryContext middlewares are applied on the received data, whenever the IoT Agent queries the Context Broker
237
+ for information. I.e.: both middlewares will be automatically applied whenever the `update()` or `query()` functions are
238
+ called in the library.
239
+
240
+ All the middlewares have the opportunity to break the chain of middleware applications by calling the `callback()` with
241
+ an error object (the usual convention). If any of the updateContext middlewares raise an error, no request will be sent
242
+ to the Context Broker. On the other hand, the queryContext request is always performed, but the call to the `query()`
243
+ function will end up in an error if any of the queryContext middlewares report an error.
244
+
245
+ ### Development
246
+
247
+ All the middlewares have the same signature:
248
+
249
+ ```javascript
250
+ function middlewareName(entity, typeInformation, callback) {}
251
+ ```
252
+
253
+ The arguments for any middleware are the NGSI data over which it can operate:
254
+
255
+ - An updateContext payload in the case of an updateContext middleware and a queryContext payload otherwise;
256
+ - a typeInformation object containing all the information about the device stored during registration.
257
+ - and the customary `callback` parameter, with the usual meaning. It's really important for the library user to call
258
+ this callback, as failing to do so may hang the IoT Agent completely. The callback must be called with the an
259
+ optional error in the first argument and the same arguments received (potentially modified) as the following.
260
+
261
+ In order to manage the middlewares to the system, the following functions can be used:
262
+
263
+ - `addUpdateMiddleware`: adds an updateContext middleware to the stack of middlewares. All the middlewares will be
264
+ applied to every call to the `update()` function. The final payload of the updateContext request will be the result
265
+ of applying all this middlewares in the order they have been defined.
266
+
267
+ - `addQueryMiddleware`: adds a queryContext middleware to the stack of middlewares. All the middlewares will be
268
+ applied to every call to the `query()` function.
269
+
270
+ - `resetMiddlewares`: remove all the middlewares from the system.
271
+
272
+ Usually, the full list of middlewares an IoT Agent will use would be added in the IoTAgent start sequence, so they
273
+ should not change a lot during the IoT lifetime.
274
+
275
+ ### Provided plugins
276
+
277
+ The library provides some plugins out of the box, in the `dataPlugins` collection. In order to load any of them, just
278
+ use the `addQueryMiddleware` and `addUpdateMiddleware` functions with the selected plugin, as in the example:
279
+
280
+ ```javascript
281
+ var iotaLib = require('iotagent-node-lib');
282
+
283
+ iotaLib.addUpdateMiddleware(iotaLib.dataPlugins.compressTimestamp.update);
284
+ iotaLib.addQueryMiddleware(iotaLib.dataPlugins.compressTimestamp.query);
285
+ ```
package/doc/howto.md CHANGED
@@ -75,12 +75,12 @@ folder of your project. Remember to change the Context Broker IP to your local C
75
75
  Now we can begin with the code of our IoT Agent. The very minimum code we need to start an IoT Agent is the following:
76
76
 
77
77
  ```javascript
78
- var iotAgentLib = require("iotagent-node-lib"),
79
- config = require("./config");
78
+ var iotAgentLib = require('iotagent-node-lib'),
79
+ config = require('./config');
80
80
 
81
81
  iotAgentLib.activate(config, function (error) {
82
82
  if (error) {
83
- console.log("There was an error activating the IOTA");
83
+ console.log('There was an error activating the IOTA');
84
84
  process.exit(1);
85
85
  }
86
86
  });
@@ -112,10 +112,10 @@ In order to add the Express dependency to your project, add the following line t
112
112
  The require section would end up like this (the standard `http` module is also needed):
113
113
 
114
114
  ```javascript
115
- var iotAgentLib = require("iotagent-node-lib"),
116
- http = require("http"),
117
- express = require("express"),
118
- config = require("./config");
115
+ var iotAgentLib = require('iotagent-node-lib'),
116
+ http = require('http'),
117
+ express = require('express'),
118
+ config = require('./config');
119
119
  ```
120
120
 
121
121
  And install the dependencies as usual with `npm install`. You will have to require both `express` and `http` in your
@@ -129,16 +129,16 @@ function initSouthbound(callback) {
129
129
  southboundServer = {
130
130
  server: null,
131
131
  app: express(),
132
- router: express.Router(),
132
+ router: express.Router()
133
133
  };
134
134
 
135
- southboundServer.app.set("port", 8080);
136
- southboundServer.app.set("host", "0.0.0.0");
135
+ southboundServer.app.set('port', 8080);
136
+ southboundServer.app.set('host', '0.0.0.0');
137
137
 
138
- southboundServer.router.get("/iot/d", manageULRequest);
138
+ southboundServer.router.get('/iot/d', manageULRequest);
139
139
  southboundServer.server = http.createServer(southboundServer.app);
140
- southboundServer.app.use("/", southboundServer.router);
141
- southboundServer.server.listen(southboundServer.app.get("port"), southboundServer.app.get("host"), callback);
140
+ southboundServer.app.use('/', southboundServer.router);
141
+ southboundServer.server.listen(southboundServer.app.get('port'), southboundServer.app.get('host'), callback);
142
142
  }
143
143
  ```
144
144
 
@@ -154,18 +154,18 @@ function manageULRequest(req, res, next) {
154
154
  iotAgentLib.retrieveDevice(req.query.i, req.query.k, function (error, device) {
155
155
  if (error) {
156
156
  res.status(404).send({
157
- message: "Couldn't find the device: " + JSON.stringify(error),
157
+ message: "Couldn't find the device: " + JSON.stringify(error)
158
158
  });
159
159
  } else {
160
160
  values = parseUl(req.query.d, device);
161
- iotAgentLib.update(device.name, device.type, "", values, device, function (error) {
161
+ iotAgentLib.update(device.name, device.type, '', values, device, function (error) {
162
162
  if (error) {
163
163
  res.status(500).send({
164
- message: "Error updating the device",
164
+ message: 'Error updating the device'
165
165
  });
166
166
  } else {
167
167
  res.status(200).send({
168
- message: "Device successfully updated",
168
+ message: 'Device successfully updated'
169
169
  });
170
170
  }
171
171
  });
@@ -190,17 +190,17 @@ function parseUl(data, device) {
190
190
  }
191
191
 
192
192
  function createAttribute(element) {
193
- var pair = element.split("|"),
193
+ var pair = element.split('|'),
194
194
  attribute = {
195
195
  name: pair[0],
196
196
  value: pair[1],
197
- type: findType(pair[0]),
197
+ type: findType(pair[0])
198
198
  };
199
199
 
200
200
  return attribute;
201
201
  }
202
202
 
203
- return data.split(",").map(createAttribute);
203
+ return data.split(',').map(createAttribute);
204
204
  }
205
205
  ```
206
206
 
@@ -227,14 +227,14 @@ show the modifications in the `activate()` function:
227
227
  ```javascript
228
228
  iotAgentLib.activate(config, function (error) {
229
229
  if (error) {
230
- console.log("There was an error activating the IOTA");
230
+ console.log('There was an error activating the IOTA');
231
231
  process.exit(1);
232
232
  } else {
233
233
  initSouthbound(function (error) {
234
234
  if (error) {
235
- console.log("Could not initialize South bound API due to the following error: %s", error);
235
+ console.log('Could not initialize South bound API due to the following error: %s', error);
236
236
  } else {
237
- console.log("Both APIs started successfully");
237
+ console.log('Both APIs started successfully');
238
238
  }
239
239
  });
240
240
  }
@@ -286,7 +286,7 @@ A HTTP request library will be needed in order to make those calls. To this exte
286
286
  used. In order to do so, add the following require statement to the initialization code:
287
287
 
288
288
  ```javascript
289
- request = require("request");
289
+ request = require('request');
290
290
  ```
291
291
 
292
292
  and add the `request` dependency to the `package.json` file:
@@ -303,11 +303,11 @@ and add the `request` dependency to the `package.json` file:
303
303
  The require section should now look like this:
304
304
 
305
305
  ```javascript
306
- var iotAgentLib = require("iotagent-node-lib"),
307
- http = require("http"),
308
- express = require("express"),
309
- request = require("request"),
310
- config = require("./config");
306
+ var iotAgentLib = require('iotagent-node-lib'),
307
+ http = require('http'),
308
+ express = require('express'),
309
+ request = require('request'),
310
+ config = require('./config');
311
311
  ```
312
312
 
313
313
  ### Implementation
@@ -321,11 +321,11 @@ for the context provisioning requests. At this point, we should provide two hand
321
321
  ```javascript
322
322
  function queryContextHandler(id, type, service, subservice, attributes, callback) {
323
323
  var options = {
324
- url: "http://127.0.0.1:9999/iot/d",
325
- method: "GET",
324
+ url: 'http://127.0.0.1:9999/iot/d',
325
+ method: 'GET',
326
326
  qs: {
327
- q: attributes.join(),
328
- },
327
+ q: attributes.join()
328
+ }
329
329
  };
330
330
 
331
331
  request(options, function (error, response, body) {
@@ -350,21 +350,21 @@ attributes). Here is the code for the `createResponse()` function:
350
350
 
351
351
  ```javascript
352
352
  function createResponse(id, type, attributes, body) {
353
- var values = body.split(","),
353
+ var values = body.split(','),
354
354
  responses = [];
355
355
 
356
356
  for (var i = 0; i < attributes.length; i++) {
357
357
  responses.push({
358
358
  name: attributes[i],
359
- type: "string",
360
- value: values[i],
359
+ type: 'string',
360
+ value: values[i]
361
361
  });
362
362
  }
363
363
 
364
364
  return {
365
365
  id: id,
366
366
  type: type,
367
- attributes: responses,
367
+ attributes: responses
368
368
  };
369
369
  }
370
370
  ```
@@ -374,11 +374,11 @@ function createResponse(id, type, attributes, body) {
374
374
  ```javascript
375
375
  function updateContextHandler(id, type, service, subservice, attributes, callback) {
376
376
  var options = {
377
- url: "http://127.0.0.1:9999/iot/d",
378
- method: "GET",
377
+ url: 'http://127.0.0.1:9999/iot/d',
378
+ method: 'GET',
379
379
  qs: {
380
- d: createQueryFromAttributes(attributes),
381
- },
380
+ d: createQueryFromAttributes(attributes)
381
+ }
382
382
  };
383
383
 
384
384
  request(options, function (error, response, body) {
@@ -388,31 +388,31 @@ function updateContextHandler(id, type, service, subservice, attributes, callbac
388
388
  callback(null, {
389
389
  id: id,
390
390
  type: type,
391
- attributes: attributes,
391
+ attributes: attributes
392
392
  });
393
393
  }
394
394
  });
395
395
  }
396
396
  ```
397
397
 
398
- The updateContext handler deals with the modification requests that arrive at the North Port of the IoT Agent via `/v2/op/update`. It is
399
- invoked once for each entity requested (note that a single request can contain multiple entity updates), with the same
400
- parameters used in the queryContext handler. The only difference is the value of the attributes array, now containing a
401
- list of attribute objects, each containing name, type and value. The handler must also make use of the callback to
402
- return a list of updated attributes.
398
+ The updateContext handler deals with the modification requests that arrive at the North Port of the IoT Agent via
399
+ `/v2/op/update`. It is invoked once for each entity requested (note that a single request can contain multiple entity
400
+ updates), with the same parameters used in the queryContext handler. The only difference is the value of the attributes
401
+ array, now containing a list of attribute objects, each containing name, type and value. The handler must also make use
402
+ of the callback to return a list of updated attributes.
403
403
 
404
404
  For this handler we have used a helper function called `createQueryFromAttributes()`, that transforms the NGSI
405
405
  representation of the attributes to the UL type expected by the device:
406
406
 
407
407
  ```javascript
408
408
  function createQueryFromAttributes(attributes) {
409
- var query = "";
409
+ var query = '';
410
410
 
411
411
  for (var i in attributes) {
412
- query += attributes[i].name + "|" + attributes[i].value;
412
+ query += attributes[i].name + '|' + attributes[i].value;
413
413
 
414
414
  if (i != attributes.length - 1) {
415
- query += ",";
415
+ query += ',';
416
416
  }
417
417
  }
418
418
 
@@ -544,10 +544,6 @@ request/seconds that can be manage by the server. It's important to remark that
544
544
  included in the IoT Agent Node Lib but it is not mandatory that you activate this functionality. In this example, we
545
545
  will see how to use this functionality to deploy an IoT Agent in multi-thread environment.
546
546
 
547
- **WARNING:** it has been observed in Orion-IOTA integration tests some fails in bidirectional plugin usage scenarios in
548
- multi-thread mode. The fail has not been confirmed yet (it could be a glitch of the testing environment). However, take
549
- this into account if you use multi-thread in combination with bidirectional plugin.
550
-
551
547
  In order to activate the functionality, you have two options, configure the `config.js` file to add the following line:
552
548
 
553
549
  ```javascript
@@ -563,9 +559,9 @@ variable and afterward the value of the multiCore in the `config.js` file. The r
563
559
  (the standard `http` module is also needed):
564
560
 
565
561
  ```javascript
566
- var iotAgent = require("../lib/iotagent-implementation"),
567
- iotAgentLib = require("iotagent-node-lib"),
568
- config = require("./config");
562
+ var iotAgent = require('../lib/iotagent-implementation'),
563
+ iotAgentLib = require('iotagent-node-lib'),
564
+ config = require('./config');
569
565
  ```
570
566
 
571
567
  It is important to mention the purpose of the `iotAgent` variable. It is the proper implementation of the IoT Agent
@@ -581,9 +577,9 @@ about starting the IoTAgent:
581
577
  ```javascript
582
578
  iotAgentLib.startServer(config, iotAgent, function (error) {
583
579
  if (error) {
584
- console.log(context, "Error starting IoT Agent: [%s] Exiting process", error);
580
+ console.log(context, 'Error starting IoT Agent: [%s] Exiting process', error);
585
581
  } else {
586
- console.log(context, "IoT Agent started");
582
+ console.log(context, 'IoT Agent started');
587
583
  }
588
584
  });
589
585
  ```
@@ -610,7 +606,7 @@ handlers themselves. Here we can see the definition of the configuration handler
610
606
 
611
607
  ```javascript
612
608
  function configurationHandler(configuration, callback) {
613
- console.log("\n\n* REGISTERING A NEW CONFIGURATION:\n%s\n\n", JSON.stringify(configuration, null, 4));
609
+ console.log('\n\n* REGISTERING A NEW CONFIGURATION:\n%s\n\n', JSON.stringify(configuration, null, 4));
614
610
  callback(null, configuration);
615
611
  }
616
612
  ```
@@ -627,8 +623,8 @@ feature, let's use the provisioning handler to change the value of the type of t
627
623
 
628
624
  ```javascript
629
625
  function provisioningHandler(device, callback) {
630
- console.log("\n\n* REGISTERING A NEW DEVICE:\n%s\n\n", JSON.stringify(device, null, 4));
631
- device.type = "CertifiedType";
626
+ console.log('\n\n* REGISTERING A NEW DEVICE:\n%s\n\n', JSON.stringify(device, null, 4));
627
+ device.type = 'CertifiedType';
632
628
  callback(null, device);
633
629
  }
634
630
  ```
@@ -73,9 +73,9 @@ overriding the group setting.
73
73
  ```
74
74
 
75
75
  When connected to an **NGSI-LD** context broker, an IoT Agent is able to indicate whether it is willing to accept `null`
76
- values and also whether it is able to process the **NGSI-LD** `datasetId` metadata element. Setting these
77
- values to `false` will cause the IoT Agent to return a 400 **Bad Request** HTTP status code explaining that the IoT
78
- Agent does not support nulls or multi-attribute requests if they are encountered.
76
+ values and also whether it is able to process the **NGSI-LD** `datasetId` metadata element. Setting these values to
77
+ `false` will cause the IoT Agent to return a 400 **Bad Request** HTTP status code explaining that the IoT Agent does not
78
+ support nulls or multi-attribute requests if they are encountered.
79
79
 
80
80
  ```javascript
81
81
  {
@@ -249,10 +249,6 @@ used for the same purpose. For instance:
249
249
  'http://192.168.56.1:4041'.
250
250
  - **iotaVersion**: indicates the version of the IoTA that will be displayed in the about method (it should be filled
251
251
  automatically by each IoTA).
252
- - **appendMode**: if this flag is activated, the update requests to the Context Broker will be performed always with
253
- APPEND type, instead of the default UPDATE. This have implications in the use of attributes with Context Providers,
254
- so this flag should be used with care. This flag is overwritten by `autoprovision` flag in group or device
255
- provision.
256
252
  - **dieOnUnexpectedError**: if this flag is activated, the IoTAgent will not capture global exception, thus dying upon
257
253
  any unexpected error.
258
254
  - **singleConfigurationMode**: enables the Single Configuration mode for backwards compatibility (see description in
@@ -277,8 +273,6 @@ used for the same purpose. For instance:
277
273
  the IoTAgent runs in a single thread. For more details about multi-core functionality, please refer to the
278
274
  [Cluster](https://nodejs.org/api/cluster.html) module in Node.js and
279
275
  [this section](howto.md#iot-agent-in-multi-thread-mode) of the library documentation.
280
- - **defaultExpressionLanguage**: the default expression language used to compute expressions, possible values are:
281
- `legacy` or `jexl`. When not set or wrongly set, `legacy` is used as default value.
282
276
  - **fallbackTenant** - For Linked Data Context Brokers which do not support multi-tenancy, this provides an
283
277
  alternative mechanism for supplying the `NGSILD-Tenant` header. Note that NGSILD-Tenant has not yet been included in
284
278
  the NGSI-LD standard (it has been proposed for the next update of the standard, but the final decision has yet been
@@ -354,7 +348,6 @@ overrides.
354
348
  | IOTA_MONGO_SSL | `mongodb.ssl` |
355
349
  | IOTA_MONGO_EXTRAARGS | `mongodb.extraArgs` |
356
350
  | IOTA_SINGLE_MODE | `singleConfigurationMode` |
357
- | IOTA_APPEND_MODE | `appendMode` |
358
351
  | IOTA_POLLING_EXPIRATION | `pollingExpiration` |
359
352
  | IOTA_POLLING_DAEMON_FREQ | `pollingDaemonFrequency` |
360
353
  | IOTA_AUTOCAST | `autocast` |
@@ -362,7 +355,6 @@ overrides.
362
355
  | IOTA_JSON_LD_CONTEXT | `jsonLdContext` |
363
356
  | IOTA_FALLBACK_TENANT | `fallbackTenant` |
364
357
  | IOTA_FALLBACK_PATH | `fallbackPath` |
365
- | IOTA_DEFAULT_EXPRESSION_LANGUAGE | `defaultExpressionLanguage` |
366
358
  | IOTA_EXPLICIT_ATTRS | `explicitAttrs` |
367
359
  | IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION | `defaultEntityNameConjunction` |
368
360
  | IOTA_RELAX_TEMPLATE_VALIDATION | `relaxTemplateValidation` |
@@ -1,4 +1,4 @@
1
1
  mkdocs==1.2.3
2
- Pygments==2.9.0
2
+ Pygments==2.15.0
3
3
  Markdown==3.3.4
4
4
  jinja2==3.0.0
@@ -1,4 +1,4 @@
1
- ARG IMAGE_TAG=11.3-slim
1
+ ARG IMAGE_TAG=12.1-slim
2
2
  FROM debian:${IMAGE_TAG}
3
3
 
4
4
  ARG CLEAN_DEV_TOOLS
@@ -153,7 +153,6 @@ function processEnvironmentVariables() {
153
153
  'IOTA_POLLING_EXPIRATION',
154
154
  'IOTA_POLLING_DAEMON_FREQ',
155
155
  'IOTA_MULTI_CORE',
156
- 'IOTA_DEFAULT_EXPRESSION_LANGUAGE',
157
156
  'IOTA_RELAX_TEMPLATE_VALIDATION',
158
157
  'IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION',
159
158
  'IOTA_JSON_LD_CONTEXT',
@@ -245,9 +244,9 @@ function processEnvironmentVariables() {
245
244
  config.contextBroker.jsonLdContext = process.env.IOTA_JSON_LD_CONTEXT.split(',').map((ctx) => ctx.trim());
246
245
  }
247
246
 
248
- if (Array.isArray(config.contextBroker.jsonLdContext) && config.contextBroker.jsonLdContext.length === 1){
247
+ if (Array.isArray(config.contextBroker.jsonLdContext) && config.contextBroker.jsonLdContext.length === 1) {
249
248
  config.contextBroker.jsonLdContext = config.contextBroker.jsonLdContext[0];
250
- }
249
+ }
251
250
 
252
251
  config.contextBroker.fallbackTenant =
253
252
  process.env.IOTA_FALLBACK_TENANT || config.contextBroker.service || 'iotagent';
@@ -265,7 +264,7 @@ function processEnvironmentVariables() {
265
264
  config.server.port = process.env.IOTA_NORTH_PORT;
266
265
  }
267
266
 
268
- config.server.ldSupport = config.server.ldSupport || {null: true, datasetId: true, merge: false};
267
+ config.server.ldSupport = config.server.ldSupport || { null: true, datasetId: true, merge: false };
269
268
 
270
269
  if (process.env.IOTA_LD_SUPPORT_NULL) {
271
270
  config.server.ldSupport.null = process.env.IOTA_LD_SUPPORT_NULL === 'true';
@@ -273,7 +272,7 @@ function processEnvironmentVariables() {
273
272
  if (process.env.IOTA_LD_SUPPORT_DATASET_ID) {
274
273
  config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_DATASET_ID === 'true';
275
274
  }
276
- if (process.env.IOTA_LD_SUPPORT_MERGE) {
275
+ if (process.env.IOTA_LD_SUPPORT_MERGE) {
277
276
  config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_MERGE === 'true';
278
277
  }
279
278
 
@@ -473,9 +472,6 @@ function processEnvironmentVariables() {
473
472
  config.multiCore = config.multiCore === true;
474
473
  }
475
474
 
476
- if (process.env.IOTA_DEFAULT_EXPRESSION_LANGUAGE) {
477
- config.defaultExpressionLanguage = process.env.IOTA_DEFAULT_EXPRESSION_LANGUAGE;
478
- }
479
475
  if (process.env.IOTA_RELAX_TEMPLATE_VALIDATION) {
480
476
  config.relaxTemplateValidation = process.env.IOTA_RELAX_TEMPLATE_VALIDATION === 'true';
481
477
  } else {
@@ -484,7 +480,9 @@ function processEnvironmentVariables() {
484
480
  if (process.env.IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION) {
485
481
  config.defaultEntityNameConjunction = process.env.IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION;
486
482
  } else {
487
- config.defaultEntityNameConjunction = config.defaultEntityNameConjunction ? config.defaultEntityNameConjunction : ':';
483
+ config.defaultEntityNameConjunction = config.defaultEntityNameConjunction
484
+ ? config.defaultEntityNameConjunction
485
+ : ':';
488
486
  }
489
487
  }
490
488
 
@@ -539,7 +537,6 @@ function ngsiVersion() {
539
537
  return 'unknown';
540
538
  }
541
539
 
542
-
543
540
  /**
544
541
  * It checks if a combination of typeInformation or common Config is LD
545
542
  *
@@ -125,16 +125,6 @@ function doActivate(newConfig, callback) {
125
125
  }
126
126
  }
127
127
 
128
- if (
129
- newConfig.defaultExpressionLanguage &&
130
- (newConfig.defaultExpressionLanguage === 'legacy' || newConfig.defaultExpressionLanguage === 'jexl')
131
- ) {
132
- logger.info(context, 'Using ' + newConfig.defaultExpressionLanguage + ' as default expression language');
133
- } else {
134
- logger.info(context, 'Default expression language not set, or invalid, using legacy configuration');
135
- newConfig.defaultExpressionLanguage = 'legacy';
136
- }
137
-
138
128
  config.setConfig(newConfig); //after chaging some configuration, we re apply the configuration
139
129
 
140
130
  logger.info(context, 'Activating IOT Agent NGSI Library.');
@@ -78,7 +78,8 @@ const map = {
78
78
  toisostring: (d) => new Date(d).toISOString(),
79
79
  // https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
80
80
  localestring: (d, timezone, options) => new Date(d).toLocaleString(timezone, options),
81
- now: () => Date.now()
81
+ now: () => Date.now(),
82
+ hextostring: (val) => new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map(byte => parseInt(byte, 16))))
82
83
  };
83
84
 
84
85
  exports.map = map;
@@ -51,7 +51,6 @@ const Device = new Schema({
51
51
  creationDate: { type: Date, default: Date.now },
52
52
  internalAttributes: Object,
53
53
  autoprovision: Boolean,
54
- expressionLanguage: String,
55
54
  explicitAttrs: Group.ExplicitAttrsType,
56
55
  ngsiVersion: String
57
56
  });
@@ -59,7 +59,6 @@ const Group = new Schema({
59
59
  attributes: Array,
60
60
  internalAttributes: Array,
61
61
  autoprovision: Boolean,
62
- expressionLanguage: String,
63
62
  explicitAttrs: ExplicitAttrsType,
64
63
  defaultEntityNameConjunction: String,
65
64
  ngsiVersion: String,