iotagent-node-lib 3.4.4 → 4.0.1
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.
- package/README.md +4 -0
- package/doc/admin.md +1 -13
- package/doc/api.md +116 -18
- package/doc/devel/architecture.md +0 -12
- package/doc/index.md +1 -1
- package/doc/roadmap.md +22 -10
- package/lib/commonConfig.js +0 -11
- package/lib/errors.js +2 -2
- package/lib/model/Device.js +2 -1
- package/lib/model/Group.js +2 -1
- package/lib/model/dbConn.js +22 -11
- package/lib/plugins/expressionPlugin.js +0 -5
- package/lib/plugins/jexlParser.js +15 -31
- package/lib/services/common/genericMiddleware.js +14 -2
- package/lib/services/common/iotManagerService.js +2 -1
- package/lib/services/devices/deviceRegistryMongoDB.js +3 -1
- package/lib/services/devices/deviceService.js +16 -21
- package/lib/services/devices/devices-NGSI-LD.js +5 -98
- package/lib/services/devices/devices-NGSI-mixed.js +0 -14
- package/lib/services/devices/devices-NGSI-v2.js +3 -0
- package/lib/services/groups/groupRegistryMemory.js +0 -25
- package/lib/services/groups/groupRegistryMongoDB.js +20 -19
- package/lib/services/groups/groupService.js +3 -14
- package/lib/services/ngsi/entities-NGSI-LD.js +82 -7
- package/lib/services/ngsi/entities-NGSI-v2.js +297 -696
- package/lib/services/ngsi/ngsiUtils.js +0 -30
- package/lib/services/northBound/deviceProvisioningServer.js +6 -3
- package/lib/templates/createDevice.json +4 -0
- package/lib/templates/createDeviceLax.json +4 -0
- package/lib/templates/deviceGroup.json +4 -0
- package/lib/templates/updateDevice.json +4 -0
- package/lib/templates/updateDeviceLax.json +4 -0
- package/package.json +6 -2
- package/test/functional/README.md +378 -0
- package/test/functional/config-test.js +70 -0
- package/test/functional/functional-tests-runner.js +126 -0
- package/test/functional/functional-tests.js +241 -0
- package/test/functional/testCases.js +2944 -0
- package/test/functional/testUtils.js +251 -0
- package/test/tools/utils.js +25 -0
- package/test/unit/mongodb/mongodb-connectionoptions-test.js +35 -22
- package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json +3 -34
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin6.json +8 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin7.json +1 -4
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin8.json +1 -6
- package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +67 -87
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +7 -13
- package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +43 -43
- package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +19 -29
- package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +0 -1
- package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +35 -46
- package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +8 -9
- package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +96 -221
- package/test/unit/ngsi-ld/provisioning/device-registration_test.js +18 -27
- package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +8 -16
- package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +0 -13
- package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin8.json +4 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin29b.json +8 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin30.json +1 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +0 -6
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +8 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34b.json +14 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +1 -11
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36b.json +13 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin37.json +8 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +1 -11
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin10b.json +37 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin11.json +0 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +0 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +0 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +4 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin4.json +0 -3
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin5.json +10 -12
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin6.json +0 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin7.json +1 -5
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin8.json +8 -12
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +0 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +0 -8
- package/test/unit/ngsiv2/examples/contextRequests/updateContextStaticAttributesMetadata.json +7 -1
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +898 -28
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +0 -4
- package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +267 -0
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +19 -21
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +21 -24
- package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +1 -21
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +4 -6
- package/CHANGES_NEXT_RELEASE +0 -0
- package/test/unit/ngsi-ld/ngsiService/autocast-test.js +0 -438
- package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +0 -381
- package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +0 -311
- package/test/unit/ngsiv2/ngsiService/autocast-test.js +0 -325
- package/test/unit/ngsiv2/ngsiService/geoproperties-test.js +0 -427
- package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +0 -217
- package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +0 -119
- 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 (`
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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](
|
|
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
|
|
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
|
-
-
|
|
30
|
-
-
|
|
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
|
-
-
|
|
38
|
-
-
|
|
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,
|
|
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))
|
package/lib/commonConfig.js
CHANGED
|
@@ -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 {
|
package/lib/errors.js
CHANGED
|
@@ -229,9 +229,9 @@ class BadAnswer {
|
|
|
229
229
|
}
|
|
230
230
|
}
|
|
231
231
|
class BadTimestamp {
|
|
232
|
-
constructor(payload) {
|
|
232
|
+
constructor(payload, entityName) {
|
|
233
233
|
this.name = 'BAD_TIMESTAMP';
|
|
234
|
-
this.message = 'Invalid ISO8601 timestamp [' + payload + ']';
|
|
234
|
+
this.message = 'Invalid ISO8601 timestamp [' + payload + '] in [' + entityName + ']';
|
|
235
235
|
this.code = 400;
|
|
236
236
|
}
|
|
237
237
|
}
|
package/lib/model/Device.js
CHANGED
package/lib/model/Group.js
CHANGED
package/lib/model/dbConn.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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.
|
|
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;
|