iotagent-node-lib 3.0.0 → 3.2.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 (122) hide show
  1. package/.github/workflows/ci.yml +1 -0
  2. package/config.js +5 -5
  3. package/doc/api.md +1540 -298
  4. package/doc/deprecated.md +3 -1
  5. package/doc/development.md +120 -0
  6. package/doc/installationguide.md +3 -6
  7. package/docker-compose-dev.yml +1 -1
  8. package/lib/commonConfig.js +7 -10
  9. package/lib/fiware-iotagent-lib.js +0 -10
  10. package/lib/jexlTranformsMap.js +2 -1
  11. package/lib/plugins/bidirectionalData.js +8 -26
  12. package/lib/plugins/expressionPlugin.js +8 -40
  13. package/lib/plugins/jexlParser.js +28 -0
  14. package/lib/services/commands/commandService.js +1 -1
  15. package/lib/services/devices/deviceService.js +2 -1
  16. package/lib/services/ngsi/entities-NGSI-LD.js +15 -73
  17. package/lib/services/ngsi/entities-NGSI-v2.js +149 -124
  18. package/lib/services/northBound/deviceProvisioningServer.js +17 -14
  19. package/lib/templates/createDevice.json +5 -2
  20. package/lib/templates/createDeviceLax.json +7 -5
  21. package/lib/templates/updateDevice.json +5 -2
  22. package/lib/templates/updateDeviceLax.json +3 -5
  23. package/package.json +2 -2
  24. package/test/unit/examples/deviceProvisioningRequests/provisionBidirectionalDevice.json +5 -5
  25. package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +1 -0
  26. package/test/unit/examples/groupProvisioningRequests/bidirectionalGroup.json +4 -4
  27. package/test/unit/general/config-multi-core-test.js +2 -1
  28. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +2 -1
  29. package/test/unit/general/deviceService-test.js +2 -1
  30. package/test/unit/general/loglevel-api_test.js +2 -1
  31. package/test/unit/general/startup-test.js +2 -1
  32. package/test/unit/general/statistics-persistence_test.js +1 -0
  33. package/test/unit/general/statistics-service_test.js +1 -0
  34. package/test/unit/lazyAndCommands/commandRegistry_test.js +1 -0
  35. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -0
  36. package/test/unit/mongodb/mongodb-connectionoptions-test.js +1 -0
  37. package/test/unit/mongodb/mongodb-group-registry-test.js +1 -0
  38. package/test/unit/mongodb/mongodb-registry-test.js +2 -1
  39. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +0 -2
  40. package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +2 -1
  41. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -1
  42. package/test/unit/ngsi-ld/general/deviceService-test.js +2 -1
  43. package/test/unit/ngsi-ld/general/https-support-test.js +2 -1
  44. package/test/unit/ngsi-ld/general/iotam-autoregistration-test.js +2 -1
  45. package/test/unit/ngsi-ld/general/startup-test.js +3 -2
  46. package/test/unit/ngsi-ld/lazyAndCommands/active-devices-attribute-update-test.js +2 -1
  47. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +5 -7
  48. package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +8 -6
  49. package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +18 -22
  50. package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +2 -1
  51. package/test/unit/ngsi-ld/ngsiService/active-devices-test.js +2 -1
  52. package/test/unit/ngsi-ld/ngsiService/autocast-test.js +2 -1
  53. package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +2 -1
  54. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +32 -34
  55. package/test/unit/ngsi-ld/ngsiService/staticAttributes-test.js +2 -1
  56. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +2 -1
  57. package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +11 -15
  58. package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +2 -1
  59. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +10 -9
  60. package/test/unit/ngsi-ld/plugins/compress-timestamp-plugin_test.js +2 -1
  61. package/test/unit/ngsi-ld/plugins/custom-plugin_test.js +152 -0
  62. package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +3 -2
  63. package/test/unit/ngsi-ld/plugins/timestamp-processing-plugin_test.js +2 -1
  64. package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +1 -0
  65. package/test/unit/ngsi-ld/provisioning/device-registration_test.js +2 -1
  66. package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +15 -12
  67. package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +1 -0
  68. package/test/unit/ngsi-ld/provisioning/provisionDeviceMultientity-test.js +1 -0
  69. package/test/unit/ngsi-ld/provisioning/removeProvisionedDevice-test.js +1 -0
  70. package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +1 -0
  71. package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +1 -0
  72. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +13 -7
  73. package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +5 -1
  74. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin31.json +0 -8
  75. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +6 -0
  76. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +20 -0
  77. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin40.json +42 -0
  78. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +32 -0
  79. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +37 -0
  80. package/test/unit/ngsiv2/examples/contextRequests/updateContextProcessTimestamp.json +7 -1
  81. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverride.json +7 -1
  82. package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +7 -1
  83. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +232 -8
  84. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +2 -1
  85. package/test/unit/ngsiv2/general/deviceService-test.js +2 -1
  86. package/test/unit/ngsiv2/general/https-support-test.js +2 -1
  87. package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +2 -1
  88. package/test/unit/ngsiv2/general/startup-test.js +2 -1
  89. package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +2 -1
  90. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +2 -1
  91. package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +8 -6
  92. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +3 -3
  93. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +3 -2
  94. package/test/unit/ngsiv2/ngsiService/autocast-test.js +2 -1
  95. package/test/unit/ngsiv2/ngsiService/queryDeviceInformationInCb-test.js +2 -1
  96. package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +2 -1
  97. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +2 -1
  98. package/test/unit/ngsiv2/plugins/alias-plugin_test.js +2 -1
  99. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +8 -7
  100. package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +2 -1
  101. package/test/unit/ngsiv2/plugins/custom-plugin_test.js +151 -0
  102. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +87 -13
  103. package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +2 -1
  104. package/test/unit/ngsiv2/provisioning/device-group-api-test.js +1 -0
  105. package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +1 -0
  106. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -3
  107. package/test/unit/ngsiv2/provisioning/device-registration_test.js +2 -1
  108. package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +15 -12
  109. package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +1 -0
  110. package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -0
  111. package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +1 -0
  112. package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +1 -0
  113. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +1 -0
  114. package/test/unit/plugins/capture-configuration-inPlugins_test.js +2 -1
  115. package/test/unit/plugins/capture-provision-inPlugins_test.js +2 -1
  116. package/doc/advanced-topics.md +0 -626
  117. package/doc/expressionLanguage.md +0 -762
  118. package/lib/plugins/expressionParser.js +0 -205
  119. package/test/unit/expressions/expression-test.js +0 -197
  120. package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +0 -881
  121. package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -950
  122. package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -294
package/doc/deprecated.md CHANGED
@@ -21,6 +21,7 @@ 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)
24
25
 
25
26
  The use of Node.js v14 is highly recommended.
26
27
 
@@ -48,4 +49,5 @@ The following table provides information about the last iotagent-node-lib versio
48
49
  | Support to Node.js v8 | 2.12.0 | April 7th, 2020 |
49
50
  | Support to Node.js v10 | 2.15.0 | February 18th, 2021 |
50
51
  | Support to Node.js v12 | 2.24.0 | September 2nd, 2022 |
51
- | Support to NGSI-LD 1.3 | 2.25.0 | January 24th, 2023 |
52
+ | Support to NGSI-LD 1.3 | 2.25.0 | January 24th, 2023 |
53
+ | Support to Legacy Expressions | 3.1.0 | April 25th, 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
+ ```
@@ -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
  {
@@ -277,8 +277,6 @@ used for the same purpose. For instance:
277
277
  the IoTAgent runs in a single thread. For more details about multi-core functionality, please refer to the
278
278
  [Cluster](https://nodejs.org/api/cluster.html) module in Node.js and
279
279
  [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
280
  - **fallbackTenant** - For Linked Data Context Brokers which do not support multi-tenancy, this provides an
283
281
  alternative mechanism for supplying the `NGSILD-Tenant` header. Note that NGSILD-Tenant has not yet been included in
284
282
  the NGSI-LD standard (it has been proposed for the next update of the standard, but the final decision has yet been
@@ -362,7 +360,6 @@ overrides.
362
360
  | IOTA_JSON_LD_CONTEXT | `jsonLdContext` |
363
361
  | IOTA_FALLBACK_TENANT | `fallbackTenant` |
364
362
  | IOTA_FALLBACK_PATH | `fallbackPath` |
365
- | IOTA_DEFAULT_EXPRESSION_LANGUAGE | `defaultExpressionLanguage` |
366
363
  | IOTA_EXPLICIT_ATTRS | `explicitAttrs` |
367
364
  | IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION | `defaultEntityNameConjunction` |
368
365
  | IOTA_RELAX_TEMPLATE_VALIDATION | `relaxTemplateValidation` |
@@ -3,6 +3,6 @@ version: '3'
3
3
  services:
4
4
 
5
5
  mongo:
6
- image: mongo:4.2
6
+ image: mongo:4.4
7
7
  ports:
8
8
  - "27017:27017"
@@ -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;
@@ -75,9 +75,7 @@ function extractVariables(item) {
75
75
 
76
76
  function extractFromExpression(value) {
77
77
  if (value.expression) {
78
- return value.expression.match(/@[a-zA-Z]+/g).map(function (item) {
79
- return item.substr(1);
80
- });
78
+ return parser.extractVariables(value.expression);
81
79
  }
82
80
  return [];
83
81
  }
@@ -157,9 +155,7 @@ function getReverseTransformations(list, values, callback) {
157
155
  let transformations = [];
158
156
 
159
157
  function getVariable(expression) {
160
- return expression.match(/@[a-zA-Z]+/g).map(function (item) {
161
- return item.substr(1);
162
- });
158
+ return parser.extractVariables(expression);
163
159
  }
164
160
 
165
161
  if (list && list.length > 0) {
@@ -191,7 +187,7 @@ function getReverseTransformations(list, values, callback) {
191
187
  */
192
188
  function processTransformationsNGSIv2(values, typeInformation, transformations, callback) {
193
189
  let cleanedExpression;
194
- const ctx = parser.extractContext(values, typeInformation);
190
+ const ctx = parser.extractContext(values);
195
191
  let resultTransformations = [];
196
192
 
197
193
  for (let j = 0; j < 2; j++) {
@@ -201,10 +197,7 @@ function processTransformationsNGSIv2(values, typeInformation, transformations,
201
197
  }
202
198
 
203
199
  for (let i = 0; i < resultTransformations.length; i++) {
204
- cleanedExpression = resultTransformations[i].expression.substr(
205
- 2,
206
- resultTransformations[i].expression.length - 3
207
- );
200
+ cleanedExpression = resultTransformations[i].expression;
208
201
 
209
202
  values.push({
210
203
  name: resultTransformations[i].object_id,
@@ -252,20 +245,12 @@ function processTransformationsNGSILD(values, typeInformation, transformations,
252
245
  // This is the direct equivalent of the NGSI-v2 function
253
246
  if (!_.isEmpty(defaultValues)) {
254
247
  for (let i = 0; i < resultTransformations.length; i++) {
255
- cleanedExpression = resultTransformations[i].expression.substr(
256
- 2,
257
- resultTransformations[i].expression.length - 3
258
- );
248
+ cleanedExpression = resultTransformations[i].expression;
259
249
  values.push({
260
250
  name: resultTransformations[i].object_id,
261
251
  type: resultTransformations[i].type,
262
252
  metadata: {},
263
- value: parser.parse(
264
- cleanedExpression,
265
- parser.extractContext(defaultValues, typeInformation),
266
- 'String',
267
- typeInformation
268
- )
253
+ value: parser.parse(cleanedExpression, parser.extractContext(defaultValues), 'String', typeInformation)
269
254
  });
270
255
  }
271
256
  }
@@ -274,10 +259,7 @@ function processTransformationsNGSILD(values, typeInformation, transformations,
274
259
  // There is no equivalent with NGSI-v2
275
260
  _.keys(datasetIds).forEach((datasetId) => {
276
261
  for (let i = 0; i < resultTransformations.length; i++) {
277
- cleanedExpression = resultTransformations[i].expression.substr(
278
- 2,
279
- resultTransformations[i].expression.length - 3
280
- );
262
+ cleanedExpression = resultTransformations[i].expression;
281
263
  values.push({
282
264
  name: resultTransformations[i].object_id,
283
265
  type: resultTransformations[i].type,
@@ -285,7 +267,7 @@ function processTransformationsNGSILD(values, typeInformation, transformations,
285
267
  metadata: {},
286
268
  value: parser.parse(
287
269
  cleanedExpression,
288
- parser.extractContext(datasetIds[datasetId], typeInformation),
270
+ parser.extractContext(datasetIds[datasetId]),
289
271
  'String',
290
272
  typeInformation
291
273
  )
@@ -24,9 +24,7 @@
24
24
  * Modified by: Federico M. Facca - Martel Innovate
25
25
  */
26
26
 
27
- const legacyParser = require('./expressionParser');
28
27
  const jexlParser = require('./jexlParser');
29
- const config = require('../commonConfig');
30
28
  /* eslint-disable no-unused-vars */
31
29
  const logger = require('logops');
32
30
  const errors = require('../errors');
@@ -44,53 +42,23 @@ function setJEXLTransforms(transformationMap) {
44
42
  }
45
43
 
46
44
  function applyExpression(expression, context, typeInformation) {
47
- let parser = jexlParser;
48
- if (!checkJexl(typeInformation)) {
49
- parser = legacyParser;
50
- }
51
- return parser.applyExpression(expression, context, typeInformation);
45
+ return jexlParser.applyExpression(expression, context, typeInformation);
52
46
  }
53
47
 
54
- function extractContext(attributeList, typeInformation) {
55
- let parser = jexlParser;
56
- if (!checkJexl(typeInformation)) {
57
- parser = legacyParser;
58
- }
59
- return parser.extractContext(attributeList);
48
+ function extractContext(attributeList) {
49
+ return jexlParser.extractContext(attributeList);
60
50
  }
61
51
 
62
- function parse(expression, context, type, typeInformation) {
63
- if (!checkJexl(typeInformation)) {
64
- return legacyParser.parse(expression, context, type);
65
- }
52
+ function parse(expression, context) {
66
53
  return jexlParser.parse(expression, context);
67
54
  }
68
55
 
69
56
  function contextAvailable(expression, context, typeInformation) {
70
- let parser = jexlParser;
71
- if (!checkJexl(typeInformation)) {
72
- parser = legacyParser;
73
- }
74
- return parser.contextAvailable(expression, context);
57
+ return jexlParser.contextAvailable(expression, context);
75
58
  }
76
59
 
77
- function checkJexl(typeInformation) {
78
- if (
79
- config.getConfig().defaultExpressionLanguage === 'jexl' &&
80
- typeInformation.expressionLanguage &&
81
- typeInformation.expressionLanguage !== 'legacy'
82
- ) {
83
- return true;
84
- } else if (config.getConfig().defaultExpressionLanguage === 'jexl' && !typeInformation.expressionLanguage) {
85
- return true;
86
- } else if (
87
- config.getConfig().defaultExpressionLanguage === 'legacy' &&
88
- typeInformation.expressionLanguage &&
89
- typeInformation.expressionLanguage === 'jexl'
90
- ) {
91
- return true;
92
- }
93
- return false;
60
+ function extractVariables(expression) {
61
+ return jexlParser.extractVariables(expression);
94
62
  }
95
63
 
96
64
  exports.parse = parse;
@@ -98,4 +66,4 @@ exports.setJEXLTransforms = setJEXLTransforms;
98
66
  exports.applyExpression = applyExpression;
99
67
  exports.extractContext = extractContext;
100
68
  exports.contextAvailable = contextAvailable;
101
- exports.checkJexl = checkJexl;
69
+ exports.extractVariables = extractVariables;
@@ -28,6 +28,8 @@
28
28
  /* eslint-disable no-unused-vars */
29
29
 
30
30
  const jexl = require('jexl');
31
+ const grammar = require('jexl/dist/grammar').getGrammar();
32
+ const Lexer = require('jexl/dist/Lexer');
31
33
  const errors = require('../errors');
32
34
  const logger = require('logops');
33
35
  const fillService = require('../services/common/domain').fillService;
@@ -62,6 +64,31 @@ function parse(expression, context, callback) {
62
64
  }
63
65
  }
64
66
 
67
+ function extractVariables(expression) {
68
+ const inst = new Lexer(grammar);
69
+ let variables = [];
70
+
71
+ try {
72
+ const tokens = inst.tokenize(expression);
73
+
74
+ // Keep only root attributes, removing the dot and sub-attributes. For example, if we have
75
+ // a.0.b, a.1.b and a.2.b, we will only keep a
76
+ // Additionaly, it will remove the function calls, since they are also detected as identifiers
77
+ variables = tokens.filter(function (token, index, array) {
78
+ return (
79
+ (token.type === ' ' && array[index - 1].type !== 'dot') ||
80
+ (token.type === 'identifier' && array[index + 1].type !== 'openParen')
81
+ );
82
+ });
83
+
84
+ // Return only array of values
85
+ return variables.map((a) => a.value);
86
+ } catch (e) {
87
+ logger.warn(logContext, 'Wrong expression found "[%j]" error: "[%s]", it will be ignored', expression, e);
88
+ return false;
89
+ }
90
+ }
91
+
65
92
  function extractContext(attributeList) {
66
93
  const context = {};
67
94
  let value;
@@ -162,6 +189,7 @@ function setTransforms(configMap) {
162
189
  logger.info(logContext, message);
163
190
  }
164
191
 
192
+ exports.extractVariables = extractVariables;
165
193
  exports.extractContext = extractContext;
166
194
  exports.contextAvailable = contextAvailable;
167
195
  exports.applyExpression = applyExpression;
@@ -58,7 +58,7 @@ function addCommandDevice(service, subservice, device, command, callback) {
58
58
  // The context for the JEXL expression should be the ID, TYPE, S, SS
59
59
  let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(device);
60
60
  attrList = device.staticAttributes ? attrList.concat(device.staticAttributes) : attrList.concat([]);
61
- let ctxt = parser.extractContext(attrList, device);
61
+ let ctxt = parser.extractContext(attrList);
62
62
  logger.debug(context, 'attrList [%j] for device %j', attrList, device);
63
63
  // expression result will be the full command payload
64
64
  let cmdValueRes = null;
@@ -294,7 +294,8 @@ function registerDevice(deviceObj, callback) {
294
294
  // Add device ID, TYPE, S, SS to the context for the JEXL expression
295
295
  let attrList = pluginUtils.getIdTypeServSubServiceFromDevice(deviceData);
296
296
  attrList = deviceData.staticAttributes ? attrList.concat(deviceData.staticAttributes) : attrList;
297
- let ctxt = expressionPlugin.extractContext(attrList, deviceData);
297
+ attrList = configuration.staticAttributes ? attrList.concat(configuration.staticAttributes) : attrList;
298
+ let ctxt = expressionPlugin.extractContext(attrList);
298
299
  try {
299
300
  entityName = expressionPlugin.applyExpression(configuration.entityNameExp, ctxt, deviceData);
300
301
  } catch (e) {