iotagent-node-lib 3.4.4 → 4.0.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 (93) hide show
  1. package/README.md +4 -0
  2. package/doc/admin.md +1 -13
  3. package/doc/api.md +116 -18
  4. package/doc/devel/architecture.md +0 -12
  5. package/doc/index.md +1 -1
  6. package/doc/roadmap.md +22 -10
  7. package/lib/commonConfig.js +0 -11
  8. package/lib/model/Device.js +2 -1
  9. package/lib/model/Group.js +2 -1
  10. package/lib/model/dbConn.js +22 -11
  11. package/lib/plugins/expressionPlugin.js +0 -5
  12. package/lib/plugins/jexlParser.js +15 -31
  13. package/lib/services/common/genericMiddleware.js +14 -2
  14. package/lib/services/common/iotManagerService.js +2 -1
  15. package/lib/services/devices/deviceRegistryMongoDB.js +3 -1
  16. package/lib/services/devices/deviceService.js +16 -21
  17. package/lib/services/devices/devices-NGSI-LD.js +5 -98
  18. package/lib/services/devices/devices-NGSI-mixed.js +0 -14
  19. package/lib/services/devices/devices-NGSI-v2.js +3 -0
  20. package/lib/services/groups/groupRegistryMemory.js +0 -25
  21. package/lib/services/groups/groupRegistryMongoDB.js +20 -19
  22. package/lib/services/groups/groupService.js +3 -14
  23. package/lib/services/ngsi/entities-NGSI-LD.js +81 -6
  24. package/lib/services/ngsi/entities-NGSI-v2.js +303 -698
  25. package/lib/services/ngsi/ngsiUtils.js +0 -30
  26. package/lib/services/northBound/deviceProvisioningServer.js +6 -3
  27. package/lib/templates/createDevice.json +4 -0
  28. package/lib/templates/createDeviceLax.json +4 -0
  29. package/lib/templates/deviceGroup.json +4 -0
  30. package/lib/templates/updateDevice.json +4 -0
  31. package/lib/templates/updateDeviceLax.json +4 -0
  32. package/package.json +6 -2
  33. package/test/functional/README.md +378 -0
  34. package/test/functional/config-test.js +70 -0
  35. package/test/functional/functional-tests-runner.js +126 -0
  36. package/test/functional/functional-tests.js +241 -0
  37. package/test/functional/testCases.js +2944 -0
  38. package/test/functional/testUtils.js +251 -0
  39. package/test/tools/utils.js +25 -0
  40. package/test/unit/mongodb/mongodb-connectionoptions-test.js +35 -22
  41. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json +3 -34
  42. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin6.json +8 -1
  43. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin7.json +1 -4
  44. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin8.json +1 -6
  45. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +67 -87
  46. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +7 -13
  47. package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +43 -43
  48. package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +19 -29
  49. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +0 -1
  50. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +35 -46
  51. package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +8 -9
  52. package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +96 -221
  53. package/test/unit/ngsi-ld/provisioning/device-registration_test.js +18 -27
  54. package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +8 -16
  55. package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +0 -13
  56. package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin8.json +4 -4
  57. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin29b.json +8 -0
  58. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin30.json +1 -1
  59. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +0 -6
  60. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +8 -0
  61. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34b.json +14 -0
  62. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +1 -11
  63. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36b.json +13 -0
  64. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin37.json +8 -0
  65. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +1 -11
  66. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin10b.json +37 -0
  67. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin11.json +0 -4
  68. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +0 -4
  69. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +0 -4
  70. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +4 -0
  71. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin4.json +0 -3
  72. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin5.json +10 -12
  73. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin6.json +0 -4
  74. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin7.json +1 -5
  75. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin8.json +8 -12
  76. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +0 -4
  77. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +0 -8
  78. package/test/unit/ngsiv2/examples/contextRequests/updateContextStaticAttributesMetadata.json +7 -1
  79. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +898 -28
  80. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +0 -4
  81. package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +267 -0
  82. package/test/unit/ngsiv2/plugins/alias-plugin_test.js +19 -21
  83. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +21 -24
  84. package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +1 -21
  85. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +4 -6
  86. package/test/unit/ngsi-ld/ngsiService/autocast-test.js +0 -438
  87. package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +0 -381
  88. package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +0 -311
  89. package/test/unit/ngsiv2/ngsiService/autocast-test.js +0 -325
  90. package/test/unit/ngsiv2/ngsiService/geoproperties-test.js +0 -427
  91. package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +0 -217
  92. package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +0 -119
  93. package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +0 -309
package/README.md CHANGED
@@ -95,6 +95,10 @@ decommission devices. [API](doc/api.md).
95
95
 
96
96
  Contributions to development can be found [here](doc/devel/development.md) - additional contributions are welcome.
97
97
 
98
+ If you are whishing to test the library, or include new tests (either as part of a contribution or as a new feature or
99
+ as a bug report), you can use the functional tests suite included in the project. The tests are described using a JSON
100
+ file. You can find more information about the test suite in the [Functional Tests Guide](test/functional/README.md).
101
+
98
102
  ### Agent Console
99
103
 
100
104
  A command-line client to experiment with the library is packed with it. The command-line client can be started using the
package/doc/admin.md CHANGED
@@ -16,14 +16,12 @@
16
16
  - [providerUrl](#providerurl)
17
17
  - [iotaVersion](#iotaversion)
18
18
  - [dieOnUnexpectedError](#dieonunexpectederror)
19
- - [singleConfigurationMode](#singleconfigurationmode)
20
19
  - [timestamp](#timestamp)
21
20
  - [defaultResource](#defaultresource)
22
21
  - [defaultKey](#defaultkey)
23
22
  - [componentName](#componentname)
24
23
  - [pollingExpiration](#pollingexpiration)
25
24
  - [pollingDaemonFrequency](#pollingdaemonfrequency)
26
- - [autocast](#autocast)
27
25
  - [multiCore](#multicore)
28
26
  - [fallbackTenant](#fallbacktenant)
29
27
  - [fallbackPath](#fallbackpath)
@@ -262,7 +260,7 @@ the `mongob` section (as described bellow). E.g.:
262
260
 
263
261
  It configures the MongoDB driver for those repositories with 'mongodb' type. If the `host` parameter is a list of
264
262
  comma-separated IPs, they will be considered to be part of a Replica Set. In that case, the optional property
265
- `replicaSet` should contain the Replica Set name. If the database requires authentication, username (`username`),
263
+ `replicaSet` should contain the Replica Set name. If the database requires authentication, username (`user`),
266
264
  password (`password`) and authSource (`authSource`) can be set. If the database requires TLS/SSL connection but any
267
265
  validation of the certificate chain is not mandatory, all you need is to set the ssl (`ssl`) option as `true` to connect
268
266
  the database. If you need to add more complex option(s) such as `retryWrites=true` or `w=majority` when connection
@@ -348,10 +346,6 @@ IoTA).
348
346
 
349
347
  if this flag is activated, the IoTAgent will not capture global exception, thus dying upon any unexpected error.
350
348
 
351
- #### `singleConfigurationMode`
352
-
353
- enables the Single Configuration mode for backwards compatibility (see description in the Overview). Default to false.
354
-
355
349
  #### `timestamp`
356
350
 
357
351
  if this flag is activated:
@@ -383,10 +377,6 @@ amount of time without being collected by the device, the expiration daemon will
383
377
  time between collection of expired commands in milliseconds. This attribute is optional (if this parameter doesn't exist
384
378
  the polling daemon won't be started).
385
379
 
386
- #### `autocast`
387
-
388
- When enabled, the IoT Agents will try to cast attribute's values considering the JSON native type (only for NGSI v2).
389
-
390
380
  #### `multiCore`
391
381
 
392
382
  When enabled, the IoT Agents runs in multi-thread environment to take advantage of multi-core systems. It allows two
@@ -483,10 +473,8 @@ overrides.
483
473
  | IOTA_MONGO_RETRY_TIME | `mongodb.retryTime` |
484
474
  | IOTA_MONGO_SSL | `mongodb.ssl` |
485
475
  | IOTA_MONGO_EXTRAARGS | `mongodb.extraArgs` |
486
- | IOTA_SINGLE_MODE | `singleConfigurationMode` |
487
476
  | IOTA_POLLING_EXPIRATION | `pollingExpiration` |
488
477
  | IOTA_POLLING_DAEMON_FREQ | `pollingDaemonFrequency` |
489
- | IOTA_AUTOCAST | `autocast` |
490
478
  | IOTA_MULTI_CORE | `multiCore` |
491
479
  | IOTA_JSON_LD_CONTEXT | `jsonLdContext` |
492
480
  | IOTA_FALLBACK_TENANT | `fallbackTenant` |
package/doc/api.md CHANGED
@@ -27,8 +27,8 @@
27
27
  - [Measurement transformation](#measurement-transformation)
28
28
  - [Measurement transformation definition](#measurement-transformation-definition)
29
29
  - [Measurement transformation execution](#measurement-transformation-execution)
30
+ - [Measurement transformation order](#measurement-transformation-order)
30
31
  - [Multientity measurement transformation support (`object_id`)](#multientity-measurement-transformation-support-object_id)
31
- - [Timestamp Compression](#timestamp-compression)
32
32
  - [Timestamp Processing](#timestamp-processing)
33
33
  - [Overriding global Context Broker host](#overriding-global-context-broker-host)
34
34
  - [Multitenancy, FIWARE Service and FIWARE ServicePath](#multitenancy-fiware-service-and-fiware-servicepath)
@@ -179,9 +179,9 @@ information coming from the device (measures) is mapped to the Context Broker at
179
179
  All of them have the same syntax, a list of objects with the following attributes:
180
180
 
181
181
  - **object_id** (optional): name of the attribute as coming from the device.
182
- - **name** (mandatory): ID of the attribute in the target entity in the Context Broker. Note that `id` and `type`
183
- are not valid attribute names at Context Broker. Thus, although a measure named `id` or `type` will not break the IOT Agent, they
184
- are silently ignored and never progress toward Context Broker entities.
182
+ - **name** (mandatory): ID of the attribute in the target entity in the Context Broker. Note that `id` and `type` are
183
+ not valid attribute names at Context Broker. Thus, although a measure named `id` or `type` will not break the IoT
184
+ Agent, they are silently ignored and never progress toward Context Broker entities.
185
185
  - **type** (mandatory): name of the type of the attribute in the target entity.
186
186
  - **metadata** (optional): additional static metadata for the attribute in the target entity. (e.g. `unitCode`)
187
187
 
@@ -211,9 +211,9 @@ Additionally for commands (which are attributes of type `command`) the following
211
211
  - **contentType**: `content-type` header used when send command by HTTP transport (ignored in other kinds of
212
212
  transports)
213
213
 
214
- Note that, when information comming from devices, this means measures, are not defined neither in the group, nor in the
215
- device, the IoT agent will store that information into the destination entity using the same attribute name than the
216
- measure name, unless `explicitAttrs` is defined. Measures `id` or `type` names are invalid, and will be ignored.
214
+ Note that, when information coming from devices, this means measures, are not defined neither in the group, nor in the
215
+ device, the IoT agent will store that information into the destination entity using the same attribute name than the
216
+ measure name, unless `explicitAttrs` is defined. Measures `id` or `type` names are invalid, and will be ignored.
217
217
 
218
218
  ## Multientity support
219
219
 
@@ -408,7 +408,7 @@ Case 1 (default):
408
408
  "explicitAttrs": false
409
409
  ```
410
410
 
411
- every measure will be propagated to NGSI interface.
411
+ every measure will be propagated to NGSI interface, including all static attributes.
412
412
 
413
413
  Case 2:
414
414
 
@@ -416,7 +416,14 @@ Case 2:
416
416
  "explicitAttrs": true
417
417
  ```
418
418
 
419
- just measures defined in active, static (plus conditionally TimeInstant) will be propagated to NGSI interface.
419
+ In this case, should only progress active and static attributes defined in the device or group provision (`TimeInstant`
420
+ attribute will be also included if enabled), including also all static attributes. In other words, having
421
+ `"explicitAttrs":true` would prevent the IoTA creating attributes into the related entity within the context broker from
422
+ measures that are not explicitly defined in the device or group provision.
423
+
424
+ Note that attributes defined in the provision that are not receiving a measure (or having a expression defined that is
425
+ resulting `null`) will not progress (this means, the NGSI request to update the entity in the context broker is not
426
+ going to include that attribute) unless `skipValue` is defined to other value than `null`
420
427
 
421
428
  Case 3:
422
429
 
@@ -426,7 +433,11 @@ Case 3:
426
433
 
427
434
  just NGSI attributes defined in the array (identified by their attribute names, not by their object_id, plus
428
435
  conditionally TimeInstant) will be propagated to NGSI interface (note that in this case the value of `explicitAttrs` is
429
- not a JSON but a JEXL Array that looks likes a JSON).
436
+ not a JSON but a JEXL Array that looks likes a JSON). Only static attributes included in that array will be propagated
437
+ to NGSI interface.
438
+
439
+ All attributes contained in the array must be defined as `attributes` or `static_attributes`. Not defined measures
440
+ (`object_id`) will be dropped, even if they are defined in the `explicitAttrs` array.
430
441
 
431
442
  Case 4:
432
443
 
@@ -436,7 +447,11 @@ Case 4:
436
447
 
437
448
  just NGSI attributes defined in the array (identified by their attribute names and/or by their object_id) will be
438
449
  propagated to NGSI interface (note that in this case the value of `explicitAttrs` is not a JSON but a JEXL Array/Object
439
- that looks likes a JSON). This is necessary when same attribute names are used within multiple entities.
450
+ that looks likes a JSON). This is necessary when same attribute names are used within multiple entities. Only static
451
+ attributes included in that array will be propagated to NGSI interface.
452
+
453
+ Note that in the previous case show above, when selecting the object_id (with `{object_id:'active_id'}`), the attribute
454
+ must be defined. In other words, it would not work if the attribute with the corresponding `object_id`, is not defined.
440
455
 
441
456
  Case 5:
442
457
 
@@ -759,6 +774,94 @@ following to CB:
759
774
 
760
775
  [Interactive expression `spaces | trim`][5]
761
776
 
777
+ ### Measurement transformation order
778
+
779
+ The IoTA executes the transformaion looping over the `attributes` provision field. Every time a new expression is
780
+ evaluated, the JEXL context is updated with the expression result. The order defined in the `attributes` array is taken
781
+ for expression evaluation. This should be considered when using **nested expressions**, that uses values calculated in
782
+ other attributes.
783
+
784
+ For example, let's consider the following provision for a device which send a measure named `level`:
785
+
786
+ ```json
787
+ "attributes": [
788
+ {
789
+ "name": "correctedLevel",
790
+ "type": "Number",
791
+ "expression": "level * 0.897"
792
+ },
793
+ {
794
+ "name": "normalizedLevel",
795
+ "type": "Number",
796
+ "expression": "correctedLevel / 100"
797
+ }
798
+ ]
799
+ ```
800
+
801
+ The expression for `correctedLevel` is evaluated first (using `level` measure as input). Next, the `normalizedLevel` is
802
+ evaluated (using `correctedLevel` calculated attribute, just calculated before).
803
+
804
+ Note that if we reserve the order, this way:
805
+
806
+ ```json
807
+ "attributes": [
808
+ {
809
+ "name": "normalizedLevel",
810
+ "type": "Number",
811
+ "expression": "correctedLevel / 100"
812
+ },
813
+ {
814
+ "name": "correctedLevel",
815
+ "type": "Number",
816
+ "expression": "level * 0.897"
817
+ },
818
+ ]
819
+ ```
820
+
821
+ It is not going to work. The first expression expects a `correctedLevel` which is neither a measure (remember the only
822
+ measure sent by the device is named `level`) nor a previously calculated attribute. Thus, `correctedLevel` will end with
823
+ a `null` value, so will not be part of the update request send to the Context Broker unless `skipValue` (check
824
+ [Devices](#devices) section avobe) is defined with a different value thant the default one (`null`).
825
+
826
+ In conclusion: **the order of attributes in the `attributes` arrays at provising time matters with regards to nested
827
+ expression evaluation**.
828
+
829
+ Let's consider the following example. It is an anti-pattern but it's quite illustrative on how ordering works:
830
+
831
+ ```json
832
+ "attributes": [
833
+ {
834
+ "name": "a",
835
+ "type": "Number",
836
+ "expression": "b*10"
837
+ },
838
+ {
839
+ "name": "b",
840
+ "type": "Number",
841
+ "expression": "a*10"
842
+ }
843
+ ]
844
+ ```
845
+
846
+ When receiving a measure with the following values:
847
+
848
+ ```json
849
+ {
850
+ "a": 10,
851
+ "b": 20
852
+ }
853
+ ```
854
+
855
+ Then, as they are executed sequentially, the first attribute expression to be evaluated will be `a`, taking the value of
856
+ the attribute `b` multiplied by 10, in this case, `200`. After that, the second attribute expression to be evaluated is
857
+ the one holded by `b`. In this case, that attribute would take 10 times the value of `a`. In that case, since the JEXL
858
+ context was updated with the lastest execution, the value of `b` will be `2000`, being update at Context Broker entity:
859
+
860
+ ```json
861
+ "a": {"value": 200, "type": "Number"},
862
+ "b": {"value": 2000, "type": "Number"}
863
+ ```
864
+
762
865
  ### Multientity measurement transformation support (`object_id`)
763
866
 
764
867
  To allow support for measurement transformation in combination with multi entity feature, where the same attribute is
@@ -846,13 +949,6 @@ Will now generate the following NGSI v2 payload:
846
949
  }
847
950
  ```
848
951
 
849
- ## Timestamp Compression
850
-
851
- This functionality changes all the timestamp attributes found in the entity, and all the timestamp metadata found in any
852
- attribute, from the basic complete calendar timestamp of the ISO8601 (e.g.: 20071103T131805) to the extended complete
853
- calendar timestamp (e.g.: +002007-11-03T13:18). The middleware expects to receive the basic format in updates and return
854
- it in queries (and viceversa, receive the extended one in queries and return it in updates).
855
-
856
952
  ## Timestamp Processing
857
953
 
858
954
  The IOTA processes the entity attributes looking for a `TimeInstant` attribute. If one is found, for NGSI v2, then it
@@ -1120,6 +1216,7 @@ Config group is represented by a JSON object with the following fields:
1120
1216
  | `ngsiVersion` | ✓ | string | | 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. |
1121
1217
  | `defaultEntityNameConjunction` | ✓ | string | | optional string value to set default conjunction string used to compose a default `entity_name` when is not provided at device provisioning time. |
1122
1218
  | `autoprovision` | ✓ | bool | ✓? | 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`. |
1219
+ | `payloadType` | ✓ | string | | optional string value used to switch between **IoTAgent**, **NGSI-v2** and **NGSI-LD** measure payloads types. Possible values are: `iotagent`, `ngsiv2` or `ngsild`. The default is `iotagent`. |
1123
1220
 
1124
1221
  ### Config group operations
1125
1222
 
@@ -1340,6 +1437,7 @@ the API resource fields and the same fields in the database model.
1340
1437
  | `internal_attributes` | ✓ | `array` | | List of internal attributes with free format for specific IoT Agent configuration. |
1341
1438
  | `explicitAttrs` | ✓ | `boolean` | ✓ | Field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](#explicitly-defined-attributes-explicitattrs) |
1342
1439
  | `ngsiVersion` | ✓ | `string` | | 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. |
1440
+ | `payloadType` | ✓ | `string` | | optional string value used to switch between **IoTAgent**, **NGSI-v2** and **NGSI-LD** measure payloads types. Possible values are: `iotagent`, `ngsiv2` or `ngsild`. The default is `iotagent`. |
1343
1441
 
1344
1442
  ### Device operations
1345
1443
 
@@ -79,18 +79,6 @@ configuration was assigned to a particular subservice and just one configuration
79
79
  relation between a Device and a Configuration didn't need the type to discriminate between Configurations. That's why
80
80
  for those agents, type was not a mandatory parameter.
81
81
 
82
- In order to allow backward-compatibility with those agents, the IoT Agent Library now implement a compatibility mode:
83
- the **Single Configuration Mode**, that makes the agent behave like the old agents. In this mode:
84
-
85
- - Each Subservice can contain just one Configuration. If a second Configuration is created for a Subservice, an error
86
- is raised.
87
-
88
- - Each Device provisioned for a Subservice is automatically assigned to the Subservice one Configuration if there is
89
- any.
90
-
91
- This compatibility has to be set for the whole IoT Agent, and there is no option of having both modes simultaneously
92
- running. Transitions from one mode to the other should be made with care, and may involve data migration.
93
-
94
82
  #### Registration
95
83
 
96
84
  Whenever a device is registered, the IoT Agent reads the device's entity information from the request or, if that
package/doc/index.md CHANGED
@@ -11,7 +11,7 @@ Broker using their own native protocols. IoT Agents should also be able to deal
11
11
  platform (authentication and authorization of the channel) and provide other common services to the device programmer.
12
12
 
13
13
  Github's [README.md](https://github.com/telefonicaid/iotagent-node-lib/blob/master/README.md) provides a good
14
- documentation summary. The [API reference](doc/api.md) and the [Development documentation](devel/development.md) cover
14
+ documentation summary. The [API reference](api.md) and the [Development documentation](devel/development.md) cover
15
15
  more advanced topics.
16
16
 
17
17
  ## Background
package/doc/roadmap.md CHANGED
@@ -13,7 +13,7 @@ only, and this section may be revised to provide newer information at any time.
13
13
 
14
14
  Disclaimer:
15
15
 
16
- - This section has been last updated in March 2022. Please take into account its content could be obsolete.
16
+ - This section has been last updated in August 2023. Please take into account its content could be obsolete.
17
17
  - Note we develop this software in Agile way, so development plan is continuously under review. Thus, this roadmap has
18
18
  to be understood as rough plan of features to be done along time which is fully valid only at the time of writing
19
19
  it. This roadmap has not be understood as a commitment on features and/or dates.
@@ -26,16 +26,22 @@ The following list of features are planned to be addressed in the short term, an
26
26
  product:
27
27
 
28
28
  - cgroup literal in configuration groups management API (community)
29
- - Metadata processing improvements
30
- - Improve command functionalities (binary data + expression + mapping)
29
+ - Refactor Append Mode & initial entity ([#1413](https://github.com/telefonicaid/iotagent-node-lib/issues/1413))
30
+ - Native support for NGSI-v2 and LD ingestion ([#1451](https://github.com/telefonicaid/iotagent-node-lib/issues/1451))
31
+ - Remove plugins structure (bidirectional plugin) ([#1413](https://github.com/telefonicaid/iotagent-node-lib/issues/1413))
32
+ - Add init and improve log traces ([#1452](https://github.com/telefonicaid/iotagent-node-lib/issues/1452))
33
+ - Remove InMemory registry for Devices and Groups ([#1429](https://github.com/telefonicaid/iotagent-node-lib/issues/1429))
31
34
 
32
35
  ### Medium term
33
36
 
34
37
  The following list of features are planned to be addressed in the medium term, typically within the subsequent
35
38
  release(s) generated in the next 9 months after the next planned release:
36
39
 
37
- - Accept JEXL Expressions for entity name in autoprovisioned devices (#1145)
38
- - Refactor entities-NGSI-v2.js module (#1166)
40
+ - Allow to add metadata to attributes from measures ([#1453](https://github.com/telefonicaid/iotagent-node-lib/issues/1453))
41
+ - Cache support ([#1467](https://github.com/telefonicaid/iotagent-node-lib/issues/1467))
42
+ - MQTT per group advanced configuration (MQTT broker, topics) ([#1454](https://github.com/telefonicaid/iotagent-node-lib/issues/1454))
43
+ - Subscription based commands ([#1455](https://github.com/telefonicaid/iotagent-node-lib/issues/1455))
44
+ - Remove registration support ([#1456](https://github.com/telefonicaid/iotagent-node-lib/issues/1456))
39
45
 
40
46
  ### Long term
41
47
 
@@ -43,23 +49,29 @@ The following list of features are proposals regarding the longer-term evolution
43
49
  development of these features has not yet been scheduled for a release in the near future. Please feel free to contact
44
50
  us if you wish to get involved in the implementation or influence the roadmap:
45
51
 
52
+ - Use the lightweight ingestion mechanism for connection oriented updates implemented in Context Broker (testing pending) ([#1457](https://github.com/telefonicaid/iotagent-node-lib/issues/1457))
53
+ - Add support to other transport protocols (BacNET, Modbus, etc) ([#1458](https://github.com/telefonicaid/iotagent-node-lib/issues/1458))
54
+ - Dynamic attribute generation (based on values array) ([#1459](https://github.com/telefonicaid/iotagent-node-lib/issues/1459))
46
55
  - Incremental introduccion of ECMAScript6 syntax (previous analysis of which sub-set of interesting aspect we want to
47
56
  take)
48
- - Use the lightweight ingestion mechanism for connection oriented updates implemented in Context Broker
49
- - Add support to other transport protocols (BacNET, Modbus, etc)
50
57
 
51
58
  ### Features already completed
52
59
 
53
60
  The following list contains all features that were in the roadmap and have already been implemented.
54
61
 
62
+ - Refactor entities-NGSI-v2.js module ([#1166](https://github.com/telefonicaid/iotagent-node-lib/issues/1166)) ([3.0.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/3.0.0))
63
+ - Accept JEXL Expressions for entity name in autoprovisioned devices (entityNameExp) ([#1145](https://github.com/telefonicaid/iotagent-node-lib/issues/1145)) ([2.22.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.22.0))
64
+ - Improve command functionalities (binary data + expression + mapping) ([2.22.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.22.0))
55
65
  - Support for "delta" measures (i.e. "temperature _increased_ in 5 degress" instead of "temperature _is_ 25")
56
66
  - Allow to handle binary messages ([iota-ul#530](https://github.com/telefonicaid/iotagent-ul/issues/530))
57
- - Removal support for NGSIv1 (#966) ([2.18.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.18.0))
67
+ - Removal support for NGSIv1 ([#966](https://github.com/telefonicaid/iotagent-node-lib/issues/966)) ([2.18.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.18.0))
58
68
  - Selectively ignore measure in the southbound interface
59
69
  ([iotagent-json#416](https://github.com/telefonicaid/iotagent-json/issues/416),
60
70
  [iotagent-ul#372](https://github.com/telefonicaid/iotagent-ul/issues/372))
61
71
  ([2.13.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.13.0))
62
- - JEXL support in expressions (#801, #687, #868)
72
+ - JEXL support in expressions ([#801](https://github.com/telefonicaid/iotagent-node-lib/issues/801),
73
+ [#687](https://github.com/telefonicaid/iotagent-node-lib/issues/687),
74
+ [#868](https://github.com/telefonicaid/iotagent-node-lib/issues/868))
63
75
  ([2.13.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.13.0))
64
- - Add MongoDB authentication support (#844)
76
+ - Add MongoDB authentication support ([#844](https://github.com/telefonicaid/iotagent-node-lib/issues/844))
65
77
  ([2.12.0](https://github.com/telefonicaid/iotagent-node-lib/releases/tag/2.12.0))
@@ -142,13 +142,11 @@ function processEnvironmentVariables() {
142
142
  'IOTA_MONGO_PORT',
143
143
  'IOTA_MONGO_DB',
144
144
  'IOTA_MONGO_REPLICASET',
145
- 'IOTA_AUTOCAST',
146
145
  'IOTA_MONGO_PASSWORD',
147
146
  'IOTA_MONGO_AUTH_SOURCE',
148
147
  'IOTA_MONGO_RETRIES',
149
148
  'IOTA_MONGO_USER',
150
149
  'IOTA_MONGO_RETRY_TIME',
151
- 'IOTA_SINGLE_MODE',
152
150
  'IOTA_POLLING_EXPIRATION',
153
151
  'IOTA_POLLING_DAEMON_FREQ',
154
152
  'IOTA_MULTI_CORE',
@@ -444,11 +442,6 @@ function processEnvironmentVariables() {
444
442
  }
445
443
  }
446
444
 
447
- // Other configuration properties
448
- if (process.env.IOTA_SINGLE_MODE) {
449
- config.singleConfigurationMode = process.env.IOTA_SINGLE_MODE === 'true';
450
- }
451
-
452
445
  if (process.env.IOTA_POLLING_EXPIRATION) {
453
446
  config.pollingExpiration = process.env.IOTA_POLLING_EXPIRATION;
454
447
  }
@@ -457,10 +450,6 @@ function processEnvironmentVariables() {
457
450
  config.pollingDaemonFrequency = process.env.IOTA_POLLING_DAEMON_FREQ;
458
451
  }
459
452
 
460
- if (process.env.IOTA_AUTOCAST) {
461
- config.autocast = process.env.IOTA_AUTOCAST === 'true';
462
- }
463
-
464
453
  if (process.env.IOTA_MULTI_CORE) {
465
454
  config.multiCore = process.env.IOTA_MULTI_CORE === 'true';
466
455
  } else {
@@ -52,7 +52,8 @@ const Device = new Schema({
52
52
  internalAttributes: Object,
53
53
  autoprovision: Boolean,
54
54
  explicitAttrs: Group.ExplicitAttrsType,
55
- ngsiVersion: String
55
+ ngsiVersion: String,
56
+ payloadType: String
56
57
  });
57
58
 
58
59
  function load(db) {
@@ -62,7 +62,8 @@ const Group = new Schema({
62
62
  explicitAttrs: ExplicitAttrsType,
63
63
  defaultEntityNameConjunction: String,
64
64
  ngsiVersion: String,
65
- entityNameExp: String
65
+ entityNameExp: String,
66
+ payloadType: String
66
67
  });
67
68
 
68
69
  function load(db) {
@@ -72,9 +72,15 @@ function init(host, db, port, options, callback) {
72
72
  return previous;
73
73
  }
74
74
 
75
+ url = 'mongodb://';
76
+
77
+ if (options.auth) {
78
+ url += options.auth.user + ':' + options.auth.password + '@';
79
+ }
80
+
75
81
  const hosts = host.split(',').map(addPort).reduce(commaConcat, '');
76
82
 
77
- url = 'mongodb://' + hosts + '/' + db;
83
+ url += hosts + '/' + db;
78
84
 
79
85
  if (options.extraArgs) {
80
86
  if (options.extraArgs instanceof Object && Object.keys(options.extraArgs).length > 0) {
@@ -112,6 +118,7 @@ function init(host, db, port, options, callback) {
112
118
  // but not sure if current mongoose version is still using mongodb 3.x internally
113
119
  // probably mongodb-connectionoptions-test.js needs to be fixed if useNewUrlParser is removed at the end
114
120
  options.useNewUrlParser = true;
121
+ options.useUnifiedTopology = true;
115
122
  mongoose.set('useCreateIndex', true);
116
123
  /* eslint-disable-next-line no-unused-vars */
117
124
  const candidateDb = mongoose.createConnection(url, options, function (error, result) {
@@ -203,16 +210,6 @@ function configureDb(callback) {
203
210
  options.replicaSet = currentConfig.mongodb.replicaSet;
204
211
  }
205
212
 
206
- if (currentConfig.mongodb.user && currentConfig.mongodb.password) {
207
- options.auth = {};
208
- options.auth.user = currentConfig.mongodb.user;
209
- options.auth.password = currentConfig.mongodb.password;
210
- }
211
-
212
- if (currentConfig.mongodb.authSource) {
213
- options.authSource = currentConfig.mongodb.authSource;
214
- }
215
-
216
213
  if (currentConfig.mongodb.ssl) {
217
214
  options.ssl = currentConfig.mongodb.ssl;
218
215
  }
@@ -221,6 +218,20 @@ function configureDb(callback) {
221
218
  options.extraArgs = currentConfig.mongodb.extraArgs;
222
219
  }
223
220
 
221
+ if (currentConfig.mongodb.user && currentConfig.mongodb.password) {
222
+ options.auth = {};
223
+ options.auth.user = currentConfig.mongodb.user;
224
+ options.auth.password = currentConfig.mongodb.password;
225
+ // authSource only applies if auth is set
226
+ if (currentConfig.mongodb.authSource) {
227
+ // Overload extraArgs if it was set
228
+ options.extraArgs = {
229
+ ...options.extraArgs,
230
+ authSource: currentConfig.mongodb.authSource
231
+ };
232
+ }
233
+ }
234
+
224
235
  init(config.getConfig().mongodb.host, dbName, port, options, callback);
225
236
  }
226
237
  } else {
@@ -57,13 +57,8 @@ function contextAvailable(expression, context, typeInformation) {
57
57
  return jexlParser.contextAvailable(expression, context);
58
58
  }
59
59
 
60
- function extractVariables(expression) {
61
- return jexlParser.extractVariables(expression);
62
- }
63
-
64
60
  exports.parse = parse;
65
61
  exports.setJEXLTransforms = setJEXLTransforms;
66
62
  exports.applyExpression = applyExpression;
67
63
  exports.extractContext = extractContext;
68
64
  exports.contextAvailable = contextAvailable;
69
- exports.extractVariables = extractVariables;
@@ -28,8 +28,6 @@
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');
33
31
  const errors = require('../errors');
34
32
  const logger = require('logops');
35
33
  const fillService = require('../services/common/domain').fillService;
@@ -47,7 +45,7 @@ function parse(expression, context, callback) {
47
45
  result = jexl.evalSync(expression, context);
48
46
  //avoid undefined result
49
47
  result = result !== undefined ? result : null;
50
- logger.debug(logContext, 'parse expression "[%j]" over "[%j]" result "[%j]" ', expression, context, result);
48
+ logger.debug(logContext, 'parse expression %j over %j result %j ', expression, context, result);
51
49
  } catch (e) {
52
50
  error = new errors.InvalidExpression(expression);
53
51
  if (callback) {
@@ -64,31 +62,6 @@ function parse(expression, context, callback) {
64
62
  }
65
63
  }
66
64
 
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] && 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
-
92
65
  function extractContext(attributeList) {
93
66
  const context = {};
94
67
  let value;
@@ -125,12 +98,24 @@ function extractContext(attributeList) {
125
98
 
126
99
  function applyExpression(expression, context, typeInformation) {
127
100
  logContext = fillService(logContext, typeInformation);
101
+ // Delete null values from context. Related:
102
+ // https://github.com/telefonicaid/iotagent-node-lib/issues/1440
103
+ // https://github.com/TomFrost/Jexl/issues/133
104
+ deleteNullsAndNaN(context);
128
105
  const result = parse(expression, context);
129
- logger.debug(logContext, 'applyExpression "[%j]" over "[%j]" result "[%j]" ', expression, context, result);
106
+ logger.debug(logContext, 'applyExpression %j over %j result %j ', expression, context, result);
130
107
  const expressionResult = result !== undefined ? result : expression;
131
108
  return expressionResult;
132
109
  }
133
110
 
111
+ function deleteNullsAndNaN(object) {
112
+ for (let key in object) {
113
+ if (object[key] === null || Number.isNaN(object[key])) {
114
+ delete object[key];
115
+ }
116
+ }
117
+ }
118
+
134
119
  function isTransform(identifier) {
135
120
  return jexl.getTransform(identifier) !== (null || undefined);
136
121
  }
@@ -141,7 +126,7 @@ function contextAvailable(expression, context) {
141
126
  jexl.evalSync(expression, context);
142
127
  return true;
143
128
  } catch (e) {
144
- logger.warn(logContext, 'Wrong expression found "[%j]" over "[%j]", it will be ignored', expression, context);
129
+ logger.info(logContext, 'Wrong expression found %j over %j, it will be ignored', expression, context);
145
130
  return false;
146
131
  }
147
132
  }
@@ -189,7 +174,6 @@ function setTransforms(configMap) {
189
174
  logger.info(logContext, message);
190
175
  }
191
176
 
192
- exports.extractVariables = extractVariables;
193
177
  exports.extractContext = extractContext;
194
178
  exports.contextAvailable = contextAvailable;
195
179
  exports.applyExpression = applyExpression;
@@ -24,8 +24,9 @@
24
24
  const logger = require('logops');
25
25
  const revalidator = require('revalidator');
26
26
  const errors = require('../../errors');
27
+ const fillService = require('./domain').fillService;
27
28
  let iotaInformation;
28
- const context = {
29
+ let context = {
29
30
  op: 'IoTAgentNGSI.GenericMiddlewares'
30
31
  };
31
32
 
@@ -39,7 +40,10 @@ const context = {
39
40
  /* eslint-disable-next-line no-unused-vars */
40
41
  function handleError(error, req, res, next) {
41
42
  let code = 500;
42
-
43
+ context = fillService(context, {
44
+ service: req.headers['fiware-service'],
45
+ subservice: req.headers['fiware-servicepath']
46
+ });
43
47
  logger.debug(context, 'Error [%s] handling request: %s', error.name, error.message);
44
48
 
45
49
  if (error.code && String(error.code).match(/^[2345]\d\d$/)) {
@@ -56,6 +60,10 @@ function handleError(error, req, res, next) {
56
60
  * Express middleware for tracing the complete request arriving to the IoTA in debug mode.
57
61
  */
58
62
  function traceRequest(req, res, next) {
63
+ context = fillService(context, {
64
+ service: req.headers['fiware-service'],
65
+ subservice: req.headers['fiware-servicepath']
66
+ });
59
67
  logger.debug(context, 'Request for path [%s] query [%j] from [%s]', req.path, req.query, req.get('host'));
60
68
 
61
69
  if (req.is('json') || req.is('application/ld+json')) {
@@ -129,6 +137,10 @@ function validateJson(template) {
129
137
  if (errorList.valid) {
130
138
  next();
131
139
  } else {
140
+ context = fillService(context, {
141
+ service: req.headers['fiware-service'],
142
+ subservice: req.headers['fiware-servicepath']
143
+ });
132
144
  logger.debug(context, 'Errors found validating request: %j', errorList);
133
145
  next(new errors.BadRequest('Errors found validating request.'));
134
146
  }