iotagent-node-lib 2.24.0 → 2.26.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.
- package/.github/workflows/ci.yml +6 -7
- package/config.js +6 -1
- package/doc/advanced-topics.md +23 -1
- package/doc/apiary/iotagent.apib +5 -5
- package/doc/deprecated.md +13 -7
- package/doc/expressionLanguage.md +44 -34
- package/doc/getting-started.md +1 -1
- package/doc/howto.md +8 -0
- package/doc/installationguide.md +18 -0
- package/doc/usermanual.md +77 -0
- package/examples/TTOpen-service.json +1 -1
- package/lib/commonConfig.js +15 -1
- package/lib/constants.js +1 -0
- package/lib/fiware-iotagent-lib.js +3 -1
- package/lib/jexlTranformsMap.js +12 -1
- package/lib/plugins/jexlParser.js +2 -2
- package/lib/services/devices/deviceService.js +20 -1
- package/lib/services/devices/devices-NGSI-v2.js +3 -1
- package/lib/services/devices/registrationUtils.js +21 -1
- package/lib/services/northBound/contextServer-NGSI-LD.js +221 -38
- package/lib/services/northBound/contextServer.js +14 -1
- package/lib/services/northBound/northboundServer.js +1 -0
- package/lib/services/northBound/restUtils.js +3 -5
- package/lib/templates/deviceGroup.json +1 -1
- package/package.json +2 -2
- package/test/unit/examples/deviceProvisioningRequests/provisionNewDeviceEmpty.json +43 -0
- package/test/unit/examples/mongoCollections/configurations.json +3 -3
- package/test/unit/expressions/jexlExpression-test.js +29 -8
- package/test/unit/general/deviceService-test.js +31 -29
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +26 -25
- package/test/unit/mongodb/mongodb-group-registry-test.js +3 -3
- package/test/unit/mongodb/mongodb-registry-test.js +30 -21
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent1.json +24 -14
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent2.json +25 -15
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent4.json +15 -5
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +9 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommandsAndLazy.json +32 -0
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice.json +12 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice2.json +9 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup.json +12 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup2.json +12 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup3.json +9 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateCommands1.json +10 -2
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent1.json +11 -1
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent2.json +12 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin30.json +11 -0
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +18 -4
- package/test/unit/ngsi-ld/general/deviceService-test.js +31 -29
- package/test/unit/ngsi-ld/general/startup-test.js +17 -2
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +1 -12
- package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +249 -0
- package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +171 -0
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +0 -3
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityJexlExpressionPlugin1.json +22 -0
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +21 -6
- package/test/unit/ngsiv2/general/deviceService-test.js +25 -23
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +58 -0
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +25 -6
package/.github/workflows/ci.yml
CHANGED
|
@@ -14,10 +14,10 @@ jobs:
|
|
|
14
14
|
steps:
|
|
15
15
|
- name: Git checkout
|
|
16
16
|
uses: actions/checkout@v2
|
|
17
|
-
- name: Use Node.js
|
|
17
|
+
- name: Use Node.js 16.x
|
|
18
18
|
uses: actions/setup-node@v1
|
|
19
19
|
with:
|
|
20
|
-
node-version:
|
|
20
|
+
node-version: 16.x
|
|
21
21
|
- name: Run Remark Markdown Linter
|
|
22
22
|
run: |
|
|
23
23
|
npm install
|
|
@@ -31,10 +31,10 @@ jobs:
|
|
|
31
31
|
steps:
|
|
32
32
|
- name: Git checkout
|
|
33
33
|
uses: actions/checkout@v2
|
|
34
|
-
- name: Use Node.js
|
|
34
|
+
- name: Use Node.js 16.x
|
|
35
35
|
uses: actions/setup-node@v1
|
|
36
36
|
with:
|
|
37
|
-
node-version:
|
|
37
|
+
node-version: 16.x
|
|
38
38
|
- name: Run EsLint Node.js Linter
|
|
39
39
|
run: |
|
|
40
40
|
npm install
|
|
@@ -51,7 +51,6 @@ jobs:
|
|
|
51
51
|
strategy:
|
|
52
52
|
matrix:
|
|
53
53
|
node-version:
|
|
54
|
-
- 12.x
|
|
55
54
|
- 14.x
|
|
56
55
|
- 16.x
|
|
57
56
|
steps:
|
|
@@ -78,10 +77,10 @@ jobs:
|
|
|
78
77
|
steps:
|
|
79
78
|
- name: Git checkout
|
|
80
79
|
uses: actions/checkout@v2
|
|
81
|
-
- name: 'Test Coverage with Node.js
|
|
80
|
+
- name: 'Test Coverage with Node.js 16.x'
|
|
82
81
|
uses: actions/setup-node@v1
|
|
83
82
|
with:
|
|
84
|
-
node-version:
|
|
83
|
+
node-version: 16.x
|
|
85
84
|
- run: |
|
|
86
85
|
npm install
|
|
87
86
|
npm run test:coverage
|
package/config.js
CHANGED
package/doc/advanced-topics.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- [Autoprovision configuration (autoprovision)](#autoprovision-configuration-autoprovision)
|
|
9
9
|
- [Explicitly defined attributes (explicitAttrs)](#explicitly-defined-attributes-explicitattrs)
|
|
10
10
|
- [Configuring operation to persist the data in Context Broker (appendMode)](#configuring-operation-to-persist-the-data-in-context-broker-appendmode)
|
|
11
|
+
- [Differences between `autoprovision`, `explicitAttrs` and `appendMode`](#differences-between-autoprovision-explicitattrs-and-appendmode)
|
|
11
12
|
- [Data mapping plugins](#data-mapping-plugins)
|
|
12
13
|
- [Development](#development)
|
|
13
14
|
- [Provided plugins](#provided-plugins)
|
|
@@ -20,7 +21,6 @@
|
|
|
20
21
|
- [Bidirectionality plugin (bidirectional)](#bidirectionality-plugin-bidirectional)
|
|
21
22
|
- [Autoprovision configuration (autoprovision)](#autoprovision-configuration-autoprovision)
|
|
22
23
|
- [Explicitly defined attributes (explicitAttrs)](#explicitly-defined-attributes-explicitattrs)
|
|
23
|
-
- [Configuring operation to persist the data in Context Broker (appendMode)](#configuring-operation-to-persist-the-data-in-context-broker-appendmode)
|
|
24
24
|
|
|
25
25
|
### Secured access to the Context Broker
|
|
26
26
|
|
|
@@ -368,6 +368,28 @@ This is a flag that can be enabled by activating the parameter `appendMode` in t
|
|
|
368
368
|
activated, the update requests to the Context Broker will be performed always with APPEND type, instead of the default
|
|
369
369
|
UPDATE. This have implications in the use of attributes with Context Providers, so this flag should be used with care.
|
|
370
370
|
|
|
371
|
+
### Differences between `autoprovision`, `explicitAttrs` and `appendMode`
|
|
372
|
+
|
|
373
|
+
Since those configuration parameters are quite similar, this section is intended to clarify the relation between them.
|
|
374
|
+
|
|
375
|
+
If `autoprovision` is set to `true` (default case), the agent will perform an initial request creating a new entity into
|
|
376
|
+
the Context Broker with **only** the static and active attributes provisioned in the config group, and also a new
|
|
377
|
+
Device in the agent, every time a measure arrives with a new `device_id`. Otherwise, this measure is ignored. This is
|
|
378
|
+
something related to the **southbound**.
|
|
379
|
+
|
|
380
|
+
What `explicitAttrs` does is to filter from the southbound the parameters that are not explicitly defined in the device
|
|
381
|
+
provision or config group. That also would avoid propagating the measures to the Context Broker.
|
|
382
|
+
|
|
383
|
+
The default way the agent updates the information into the Context Broker is by using an update request. If
|
|
384
|
+
`appendMode=true`, the IoTA will use an append request instead of an update one. This means it will store
|
|
385
|
+
the attributes even if they are not present in the entity. This seems the same functionality that the one provided by
|
|
386
|
+
`autoprovision`, but it is a different concept since the scope of this config is to setup how the IoT interacts with the
|
|
387
|
+
context broker, this is something related to the **northbound**.
|
|
388
|
+
|
|
389
|
+
Note that, even creating a group with `autoprovision=true` and `explicitAttrs=true`, if you do not provision previously
|
|
390
|
+
the entity in the Context Broker (having all attributes to be updated), it would fail if `appendMode=false`. For further
|
|
391
|
+
information check the issue [#1301](https://github.com/telefonicaid/iotagent-node-lib/issues/1301).
|
|
392
|
+
|
|
371
393
|
### Data mapping plugins
|
|
372
394
|
|
|
373
395
|
The IoT Agent Library provides a plugin mechanism in order to facilitate reusing code that makes small transformations
|
package/doc/apiary/iotagent.apib
CHANGED
|
@@ -80,7 +80,7 @@ Fields in JSON object representing a configuration group are:
|
|
|
80
80
|
|:-----------------------|:------------------------------------------------------------|
|
|
81
81
|
|`apikey` |It is a key used for devices belonging to this service. If "", service does not use apikey, but it must be specified.|
|
|
82
82
|
|`token` |If authentication/authorization system is configured, IoT Agent works as user when it publishes information. That token allows that other components to verify the identity of IoT Agent. Depends on authentication and authorization system.|
|
|
83
|
-
|`
|
|
83
|
+
|`cbHost` |Context Broker endpoint assigned to this service, it must be a real uri.|
|
|
84
84
|
|`outgoing_route` |It is an identifier for VPN/GRE tunnel. It is used when device is into a VPN and a command is sent.|
|
|
85
85
|
|`resource` |Path in IoTAgent. When protocol is HTTP a device could send information to this uri. In general, it is a uri in a HTTP server needed to load and execute a module.|
|
|
86
86
|
|`entity_type` |Entity type used in entity publication (overload default).|
|
|
@@ -126,7 +126,7 @@ Retrieve a configuration group.
|
|
|
126
126
|
"service": "service2",
|
|
127
127
|
"service_path": "/srvpath2",
|
|
128
128
|
"token": "token2",
|
|
129
|
-
"
|
|
129
|
+
"cbHost": "http://127.0.0.1:1026",
|
|
130
130
|
"entity_type": "thing",
|
|
131
131
|
"resource": "/iot/d"
|
|
132
132
|
}
|
|
@@ -152,7 +152,7 @@ Retrieve a configuration group.
|
|
|
152
152
|
"service": "service2",
|
|
153
153
|
"service_path": "/srvpath2",
|
|
154
154
|
"token": "token2",
|
|
155
|
-
"
|
|
155
|
+
"cbHost": "http://127.0.0.1:1026",
|
|
156
156
|
"entity_type": "thing",
|
|
157
157
|
"resource": "/iot/d"
|
|
158
158
|
}
|
|
@@ -162,7 +162,7 @@ Retrieve a configuration group.
|
|
|
162
162
|
|
|
163
163
|
### Create a configuration group [POST]
|
|
164
164
|
|
|
165
|
-
Create a new configuration group. From service model, mandatory fields are: apikey, resource (
|
|
165
|
+
Create a new configuration group. From service model, mandatory fields are: apikey, resource (cbHost field is temporary mandatory).
|
|
166
166
|
|
|
167
167
|
+ Request (application/json)
|
|
168
168
|
|
|
@@ -178,7 +178,7 @@ Create a new configuration group. From service model, mandatory fields are: apik
|
|
|
178
178
|
{
|
|
179
179
|
"apikey": "apikey3",
|
|
180
180
|
"token": "token2",
|
|
181
|
-
"
|
|
181
|
+
"cbHost": "http://127.0.0.1:1026",
|
|
182
182
|
"entity_type": "thing",
|
|
183
183
|
"resource": "/iot/d"
|
|
184
184
|
}
|
package/doc/deprecated.md
CHANGED
|
@@ -12,13 +12,17 @@ longer. In particular:
|
|
|
12
12
|
|
|
13
13
|
A list of deprecated features and the version in which they were deprecated follows:
|
|
14
14
|
|
|
15
|
-
- Support to NGSI v1.
|
|
15
|
+
- Support to NGSI v1 (finally removed in 2.18.0)
|
|
16
16
|
- Support to Node.js v4 in iotagent-node-lib 2.8.1 (finally removed in 2.9.0)
|
|
17
17
|
- Support to Node.js v6 in iotagent-node-lib 2.9.0 (finally removed in 2.10.0)
|
|
18
18
|
- Support to Node.js v8 in iotagent-node-lib 2.12.0 (finally removed in 2.13.0)
|
|
19
19
|
- Support to Node.js v10 in iotagent-node-lib 2.15.0 (finally removed in 2.16.0)
|
|
20
|
+
- Support to Node.js v12 in iotagent-node-lib 2.24.0 (finally removed in 2.25.0)
|
|
21
|
+
- Support to NGSI-LD v1.3 in iotagent-node-lib 2.25.0 (finally removed in 2.26.0)
|
|
22
|
+
- Support groups (provision) statically defined by configuration
|
|
23
|
+
- Support to in-memory registry (i.e.`deviceRegistry.type=memory`)
|
|
20
24
|
|
|
21
|
-
The use of Node.js
|
|
25
|
+
The use of Node.js v14 is highly recommended.
|
|
22
26
|
|
|
23
27
|
## Using old iotagent-node-lib versions
|
|
24
28
|
|
|
@@ -38,8 +42,10 @@ The following table provides information about the last iotagent-node-lib versio
|
|
|
38
42
|
|
|
39
43
|
| **Removed feature** | **Last iotagent-node-lib version supporting feature** | **That version release date** |
|
|
40
44
|
| ---------------------- | ----------------------------------------------------- | ----------------------------- |
|
|
41
|
-
| NGSI v1 API |
|
|
42
|
-
| Support to Node.js v4 | 2.8.1 | December 19th, 2018
|
|
43
|
-
| Support to Node.js v6 | 2.9.0 | May 22nd, 2019
|
|
44
|
-
| Support to Node.js v8 | 2.12.0 | April 7th, 2020
|
|
45
|
-
| Support to Node.js v10 | 2.15.0
|
|
45
|
+
| NGSI v1 API | 2.17.0 | August 30th, 2021 |
|
|
46
|
+
| Support to Node.js v4 | 2.8.1 | December 19th, 2018 |
|
|
47
|
+
| Support to Node.js v6 | 2.9.0 | May 22nd, 2019 |
|
|
48
|
+
| Support to Node.js v8 | 2.12.0 | April 7th, 2020 |
|
|
49
|
+
| Support to Node.js v10 | 2.15.0 | February 18th, 2021 |
|
|
50
|
+
| Support to Node.js v12 | 2.24.0 | September 2nd, 2022 |
|
|
51
|
+
| Support to NGSI-LD 1.3 | 2.25.0 | January 24th, 2023 |
|
|
@@ -75,7 +75,7 @@ can check the following example:
|
|
|
75
75
|
"services": [
|
|
76
76
|
{
|
|
77
77
|
"apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
|
|
78
|
-
"
|
|
78
|
+
"cbHost": "http://orion:1026",
|
|
79
79
|
"entity_type": "Thing",
|
|
80
80
|
"resource": "/iot/d"
|
|
81
81
|
"expressionLanguage": "jexl",
|
|
@@ -459,39 +459,49 @@ to incorporate new transformations from the IoT Agent configuration file in a fa
|
|
|
459
459
|
|
|
460
460
|
Current common transformation set:
|
|
461
461
|
|
|
462
|
-
| JEXL Transformation
|
|
463
|
-
|
|
|
464
|
-
| jsonparse: (str)
|
|
465
|
-
| jsonstringify: (obj)
|
|
466
|
-
| indexOf: (val, char)
|
|
467
|
-
| length: (val)
|
|
468
|
-
| trim: (val)
|
|
469
|
-
| substr: (val, int1, int2)
|
|
470
|
-
| addreduce: (arr)
|
|
471
|
-
| lengtharray: (arr)
|
|
472
|
-
| typeof: (val)
|
|
473
|
-
| isarray: (arr)
|
|
474
|
-
| isnan: (val)
|
|
475
|
-
| parseint: (val)
|
|
476
|
-
| parsefloat: (val)
|
|
477
|
-
| toisodate: (val)
|
|
478
|
-
| timeoffset:(isostr)
|
|
479
|
-
| tostring: (val)
|
|
480
|
-
| urlencode: (val)
|
|
481
|
-
| urldecode: (val)
|
|
482
|
-
| replacestr: (str, from, to)
|
|
483
|
-
| replaceregexp: (str, reg, to)
|
|
484
|
-
| replaceallstr: (str, from, to)
|
|
485
|
-
| replaceallregexp: (str, reg, to)
|
|
486
|
-
| split: (str, ch)
|
|
487
|
-
|
|
|
488
|
-
|
|
|
489
|
-
|
|
|
490
|
-
|
|
|
491
|
-
|
|
|
492
|
-
|
|
|
493
|
-
|
|
|
494
|
-
|
|
|
462
|
+
| JEXL Transformation | Equivalent JavaScript Function |
|
|
463
|
+
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- |
|
|
464
|
+
| jsonparse: (str) | `JSON.parse(str);` |
|
|
465
|
+
| jsonstringify: (obj) | `JSON.stringify(obj);` |
|
|
466
|
+
| indexOf: (val, char) | `String(val).indexOf(char);` |
|
|
467
|
+
| length: (val) | `String(val).length;` |
|
|
468
|
+
| trim: (val) | `String(val).trim();` |
|
|
469
|
+
| substr: (val, int1, int2) | `String(val).substr(int1, int2);` |
|
|
470
|
+
| addreduce: (arr) | <code>arr.reduce((i, v) | i + v));</code> |
|
|
471
|
+
| lengtharray: (arr) | `arr.length;` |
|
|
472
|
+
| typeof: (val) | `typeof val;` |
|
|
473
|
+
| isarray: (arr) | `Array.isArray(arr);` |
|
|
474
|
+
| isnan: (val) | `isNaN(val);` |
|
|
475
|
+
| parseint: (val) | `parseInt(val);` |
|
|
476
|
+
| parsefloat: (val) | `parseFloat(val);` |
|
|
477
|
+
| toisodate: (val) | `new Date(val).toISOString();` |
|
|
478
|
+
| timeoffset:(isostr) | `new Date(isostr).getTimezoneOffset();` |
|
|
479
|
+
| tostring: (val) | `val.toString();` |
|
|
480
|
+
| urlencode: (val) | `encodeURI(val);` |
|
|
481
|
+
| urldecode: (val) | `decodeURI(val);` |
|
|
482
|
+
| replacestr: (str, from, to) | `str.replace(from, to);` |
|
|
483
|
+
| replaceregexp: (str, reg, to) | `str.replace(new RegExp(reg), to);` |
|
|
484
|
+
| replaceallstr: (str, from, to) | `str.replaceAll(from, to);` |
|
|
485
|
+
| replaceallregexp: (str, reg, to) | `str.replaceAll(new RegExp(reg,"g"), to);` |
|
|
486
|
+
| split: (str, ch) | `str.split(ch);` |
|
|
487
|
+
| joinarrtostr: (arr, ch) | `arr.join(ch);` |
|
|
488
|
+
| concatarr: (arr, arr2) | `arr.concat(arr2);` |
|
|
489
|
+
| mapper: (val, values, choices) | <code>choices[values.findIndex((target) | target == val)]);</code> |
|
|
490
|
+
| thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) | (acc==0)||acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
|
|
491
|
+
| bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="|"?parseInt(i)|mask: op==="^"?parseInt(i)^mask:i)>>shf;</code> |
|
|
492
|
+
| slice: (arr, init, end) | `arr.slice(init,end);` |
|
|
493
|
+
| addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
|
|
494
|
+
| removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
|
|
495
|
+
| touppercase: (val) | `String(val).toUpperCase()` |
|
|
496
|
+
| tolowercase: (val) | `String(val).toLowerCase()` |
|
|
497
|
+
| round: (val) | `Math.round(val)` |
|
|
498
|
+
| floor: (val) | `Math.floor(val)` |
|
|
499
|
+
| ceil: (val) | `Math.ceil(val)` |
|
|
500
|
+
| tofixed: (val, decimals) | `Number.parseFloat(val).toFixed(decimals)` |
|
|
501
|
+
| gettime: (d) | `new Date(d).getTime()` |
|
|
502
|
+
| toisostring: (d) | `new Date(d).toISOString()` |
|
|
503
|
+
| localestring: (d, timezone, options) | `new Date(d).toLocaleString(timezone, options)` |
|
|
504
|
+
| now: () | `Date.now()` |
|
|
495
505
|
|
|
496
506
|
You have available this [JEXL interactive playground][99] with all the transformations already loaded, in which you can
|
|
497
507
|
test all the functions described above.
|
package/doc/getting-started.md
CHANGED
package/doc/howto.md
CHANGED
|
@@ -430,6 +430,14 @@ iotAgentLib.setDataUpdateHandler(updateContextHandler);
|
|
|
430
430
|
iotAgentLib.setDataQueryHandler(queryContextHandler);
|
|
431
431
|
```
|
|
432
432
|
|
|
433
|
+
Where necessary, additional handlers to deal with command actuations and merge-patch operations may also be added when
|
|
434
|
+
necessary.
|
|
435
|
+
|
|
436
|
+
```javascript
|
|
437
|
+
iotAgentLib.setCommandHandler(commandHandler);
|
|
438
|
+
iotAgentLib.setMergePatchHandler(mergePatchHandler);
|
|
439
|
+
```
|
|
440
|
+
|
|
433
441
|
#### IOTA Testing
|
|
434
442
|
|
|
435
443
|
In order to test it, we need to create an HTTP server simulating the device. The quickest way to do that may be using
|
package/doc/installationguide.md
CHANGED
|
@@ -72,6 +72,22 @@ overriding the group setting.
|
|
|
72
72
|
}
|
|
73
73
|
```
|
|
74
74
|
|
|
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.
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
{
|
|
82
|
+
baseRoot: '/',
|
|
83
|
+
port: 4041,
|
|
84
|
+
ldSupport : {
|
|
85
|
+
null: true,
|
|
86
|
+
datasetId: true
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
75
91
|
- **stats**: configure the periodic collection of statistics. Use `interval` in milliseconds to set the time between
|
|
76
92
|
stats writings.
|
|
77
93
|
|
|
@@ -301,6 +317,8 @@ overrides.
|
|
|
301
317
|
| IOTA_CB_NGSI_VERSION | `contextBroker.ngsiVersion` |
|
|
302
318
|
| IOTA_NORTH_HOST | `server.host` |
|
|
303
319
|
| IOTA_NORTH_PORT | `server.port` |
|
|
320
|
+
| IOTA_LD_SUPPORT_NULL | `server.ldSupport.null` |
|
|
321
|
+
| IOTA_LD_SUPPORT_DATASET_ID | `server.ldSupport.datasetId` |
|
|
304
322
|
| IOTA_PROVIDER_URL | `providerUrl` |
|
|
305
323
|
| IOTA_AUTH_ENABLED | `authentication.enabled` |
|
|
306
324
|
| IOTA_AUTH_TYPE | `authentication.type` |
|
package/doc/usermanual.md
CHANGED
|
@@ -354,6 +354,83 @@ values.
|
|
|
354
354
|
The handler is expected to call its callback once with no parameters (failing to do so may cause unexpected behaviors in
|
|
355
355
|
the IoT Agent).
|
|
356
356
|
|
|
357
|
+
##### iotagentLib.setCommandHandler()
|
|
358
|
+
|
|
359
|
+
###### Signature
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
function setCommandHandler(newHandler)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
###### Description
|
|
366
|
+
|
|
367
|
+
Sets the new user handler for registered entity commands. This handler will be called whenever a command request arrives, with
|
|
368
|
+
the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve
|
|
369
|
+
all the corresponding information from the devices and return a NGSI entity with the requested values.
|
|
370
|
+
|
|
371
|
+
The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
callback(null, {
|
|
375
|
+
type: "TheType",
|
|
376
|
+
isPattern: false,
|
|
377
|
+
id: "EntityID",
|
|
378
|
+
attributes: [
|
|
379
|
+
{
|
|
380
|
+
name: "lumniscence",
|
|
381
|
+
type: "Lumens",
|
|
382
|
+
value: "432"
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
In the case of NGSI requests affecting multiple entities, this handler will be called multiple times, one for each
|
|
389
|
+
entity, and all the results will be combined into a single response. Only IoT Agents which deal with actuator devices will include a handler for commands.
|
|
390
|
+
|
|
391
|
+
###### Params
|
|
392
|
+
|
|
393
|
+
- newHandler: User handler for command requests.
|
|
394
|
+
|
|
395
|
+
##### iotagentLib.setMergePatchHandler()
|
|
396
|
+
|
|
397
|
+
###### Signature
|
|
398
|
+
|
|
399
|
+
```javascript
|
|
400
|
+
function setMergePatchHandler(newHandler)
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
###### Description
|
|
404
|
+
|
|
405
|
+
Sets the new user handler for NGSI-LD Entity [merge-patch](https://datatracker.ietf.org/doc/html/rfc7386) requests. This handler will be called whenever a merge-patch request arrives, with
|
|
406
|
+
the following parameters: (`id`, `type`, `service`, `subservice`, `attributes`, `callback`). The handler must retrieve
|
|
407
|
+
all the corresponding information from the devices and return a NGSI entity with the requested values.
|
|
408
|
+
|
|
409
|
+
The callback must be invoked with the updated Context Element, using the information retrieved from the devices. E.g.:
|
|
410
|
+
|
|
411
|
+
```javascript
|
|
412
|
+
callback(null, {
|
|
413
|
+
type: "TheType",
|
|
414
|
+
isPattern: false,
|
|
415
|
+
id: "EntityID",
|
|
416
|
+
attributes: [
|
|
417
|
+
{
|
|
418
|
+
name: "lumniscence",
|
|
419
|
+
type: "Lumens",
|
|
420
|
+
value: "432"
|
|
421
|
+
}
|
|
422
|
+
]
|
|
423
|
+
});
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
In the case of NGSI-LD requests affecting multiple entities, this handler will be
|
|
427
|
+
called multiple times. Since merge-patch is an advanced function, not all IoT Agents
|
|
428
|
+
will include a handler for merge-patch.
|
|
429
|
+
|
|
430
|
+
###### Params
|
|
431
|
+
|
|
432
|
+
- newHandler: User handler for merge-patch requests.
|
|
433
|
+
|
|
357
434
|
##### iotagentLib.setProvisioningHandler()
|
|
358
435
|
|
|
359
436
|
###### Signature
|
package/lib/commonConfig.js
CHANGED
|
@@ -158,7 +158,9 @@ function processEnvironmentVariables() {
|
|
|
158
158
|
'IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION',
|
|
159
159
|
'IOTA_JSON_LD_CONTEXT',
|
|
160
160
|
'IOTA_FALLBACK_TENANT',
|
|
161
|
-
'IOTA_FALLBACK_PATH'
|
|
161
|
+
'IOTA_FALLBACK_PATH',
|
|
162
|
+
'IOTA_LD_SUPPORT_NULL',
|
|
163
|
+
'IOTA_LD_SUPPORT_DATASET_ID'
|
|
162
164
|
];
|
|
163
165
|
const iotamVariables = [
|
|
164
166
|
'IOTA_IOTAM_URL',
|
|
@@ -263,6 +265,18 @@ function processEnvironmentVariables() {
|
|
|
263
265
|
config.server.port = process.env.IOTA_NORTH_PORT;
|
|
264
266
|
}
|
|
265
267
|
|
|
268
|
+
config.server.ldSupport = config.server.ldSupport || {null: true, datasetId: true, merge: false};
|
|
269
|
+
|
|
270
|
+
if (process.env.IOTA_LD_SUPPORT_NULL) {
|
|
271
|
+
config.server.ldSupport.null = process.env.IOTA_LD_SUPPORT_NULL === 'true';
|
|
272
|
+
}
|
|
273
|
+
if (process.env.IOTA_LD_SUPPORT_DATASET_ID) {
|
|
274
|
+
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_DATASET_ID === 'true';
|
|
275
|
+
}
|
|
276
|
+
if (process.env.IOTA_LD_SUPPORT_MERGE) {
|
|
277
|
+
config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_MERGE === 'true';
|
|
278
|
+
}
|
|
279
|
+
|
|
266
280
|
if (process.env.IOTA_PROVIDER_URL) {
|
|
267
281
|
config.providerUrl = process.env.IOTA_PROVIDER_URL;
|
|
268
282
|
}
|
package/lib/constants.js
CHANGED
|
@@ -60,6 +60,7 @@ module.exports = {
|
|
|
60
60
|
SUBSERVICE_HEADER: 'fiware-servicepath',
|
|
61
61
|
NGSI_LD_TENANT_HEADER: 'NGSILD-Tenant',
|
|
62
62
|
NGSI_LD_PATH_HEADER: 'NGSILD-Path',
|
|
63
|
+
NGSI_LD_NULL: 'urn:ngsi-ld:null',
|
|
63
64
|
//FIXME: check Keystone support this in lowercase, then change
|
|
64
65
|
AUTH_HEADER: 'X-Auth-Token',
|
|
65
66
|
X_FORWARDED_FOR_HEADER: 'x-forwarded-for',
|
|
@@ -323,6 +323,7 @@ exports.getConfigurationSilently = groupConfig.getSilently;
|
|
|
323
323
|
exports.findConfiguration = groupConfig.find;
|
|
324
324
|
exports.setDataUpdateHandler = contextServer.setUpdateHandler;
|
|
325
325
|
exports.setCommandHandler = contextServer.setCommandHandler;
|
|
326
|
+
exports.setMergePatchHandler = contextServer.setMergePatchHandler;
|
|
326
327
|
exports.setDataQueryHandler = contextServer.setQueryHandler;
|
|
327
328
|
exports.setConfigurationHandler = contextServer.setConfigurationHandler;
|
|
328
329
|
exports.setRemoveConfigurationHandler = contextServer.setRemoveConfigurationHandler;
|
|
@@ -363,7 +364,8 @@ exports.dataPlugins = {
|
|
|
363
364
|
timestampProcess: require('./plugins/timestampProcessPlugin'),
|
|
364
365
|
expressionTransformation: require('./plugins/expressionPlugin'),
|
|
365
366
|
multiEntity: require('./plugins/multiEntity'),
|
|
366
|
-
bidirectionalData: require('./plugins/bidirectionalData')
|
|
367
|
+
bidirectionalData: require('./plugins/bidirectionalData'),
|
|
368
|
+
utils: require('./plugins/pluginUtils')
|
|
367
369
|
};
|
|
368
370
|
|
|
369
371
|
exports.alarms = require('./services/common/alarmManagement');
|
package/lib/jexlTranformsMap.js
CHANGED
|
@@ -49,6 +49,8 @@ const map = {
|
|
|
49
49
|
replaceallstr: (str, from, to) => str.replaceAll(from, to),
|
|
50
50
|
replaceallregexp: (str, reg, to) => str.replaceAll(new RegExp(reg, 'g'), to),
|
|
51
51
|
split: (str, ch) => str.split(ch),
|
|
52
|
+
joinarrtostr: (arr, ch) => arr.join(ch),
|
|
53
|
+
concatarr: (arr, arr2) => arr.concat(arr2),
|
|
52
54
|
mapper: (val, values, choices) => choices[values.findIndex((target) => target === val)],
|
|
53
55
|
thmapper: (val, values, choices) =>
|
|
54
56
|
choices[
|
|
@@ -67,7 +69,16 @@ const map = {
|
|
|
67
69
|
return Array.from(s);
|
|
68
70
|
},
|
|
69
71
|
touppercase: (val) => String(val).toUpperCase(),
|
|
70
|
-
tolowercase: (val) => String(val).toLowerCase()
|
|
72
|
+
tolowercase: (val) => String(val).toLowerCase(),
|
|
73
|
+
floor: (val) => Math.floor(val),
|
|
74
|
+
ceil: (val) => Math.ceil(val),
|
|
75
|
+
round: (val) => Math.round(val),
|
|
76
|
+
tofixed: (val, decimals) => Number.parseFloat(val).toFixed(decimals),
|
|
77
|
+
gettime: (d) => new Date(d).getTime(),
|
|
78
|
+
toisostring: (d) => new Date(d).toISOString(),
|
|
79
|
+
// https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
|
|
80
|
+
localestring: (d, timezone, options) => new Date(d).toLocaleString(timezone, options),
|
|
81
|
+
now: () => Date.now()
|
|
71
82
|
};
|
|
72
83
|
|
|
73
84
|
exports.map = map;
|
|
@@ -141,8 +141,8 @@ function contextAvailable(expression, context) {
|
|
|
141
141
|
jexl.evalSync(expression, context);
|
|
142
142
|
return true;
|
|
143
143
|
} catch (e) {
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
logger.warn(logContext, 'Wrong expression found "[%j]" over "[%j]", it will be ignored', expression, context);
|
|
145
|
+
return false;
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
@@ -132,7 +132,14 @@ function mergeArrays(original, newArray) {
|
|
|
132
132
|
* @param {Object} configuration Configuration data.
|
|
133
133
|
*/
|
|
134
134
|
function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuration, callback) {
|
|
135
|
-
logger.debug(
|
|
135
|
+
logger.debug(
|
|
136
|
+
context,
|
|
137
|
+
'deviceData before merge with conf: %j defaults: %j fields: %j configuration %j',
|
|
138
|
+
deviceData,
|
|
139
|
+
defaults,
|
|
140
|
+
fields,
|
|
141
|
+
configuration
|
|
142
|
+
);
|
|
136
143
|
for (let i = 0; i < fields.length; i++) {
|
|
137
144
|
const confField = fields[i] === 'active' ? 'attributes' : fields[i];
|
|
138
145
|
if (deviceData && deviceData[fields[i]] && ['active', 'lazy', 'commands'].indexOf(fields[i]) >= 0) {
|
|
@@ -356,12 +363,16 @@ function registerDevice(deviceObj, callback) {
|
|
|
356
363
|
deviceObj.type = deviceData.type;
|
|
357
364
|
deviceObj.staticAttributes = deviceData.staticAttributes;
|
|
358
365
|
deviceObj.commands = deviceData.commands;
|
|
366
|
+
deviceObj.lazy = deviceData.lazy;
|
|
359
367
|
if ('timestamp' in deviceData && deviceData.timestamp !== undefined) {
|
|
360
368
|
deviceObj.timestamp = deviceData.timestamp;
|
|
361
369
|
}
|
|
362
370
|
if ('explicitAttrs' in deviceData && deviceData.explicitAttrs !== undefined) {
|
|
363
371
|
deviceObj.explicitAttrs = deviceData.explicitAttrs;
|
|
364
372
|
}
|
|
373
|
+
if ('apikey' in deviceData && deviceData.apikey !== undefined) {
|
|
374
|
+
deviceObj.apikey = deviceData.apikey;
|
|
375
|
+
}
|
|
365
376
|
config.getRegistry().store(deviceObj, callback);
|
|
366
377
|
}
|
|
367
378
|
}
|
|
@@ -619,8 +630,16 @@ function findOrCreate(deviceId, group, callback) {
|
|
|
619
630
|
if ('expressionLanguage' in group && group.expressionLanguage !== undefined) {
|
|
620
631
|
newDevice.expressionLanguage = group.expressionLanguage;
|
|
621
632
|
}
|
|
633
|
+
if (
|
|
634
|
+
(!('apikey' in newDevice) || newDevice.apikey === undefined) &&
|
|
635
|
+
'apikey' in group &&
|
|
636
|
+
group.apikey !== undefined
|
|
637
|
+
) {
|
|
638
|
+
newDevice.apikey = group.apikey;
|
|
639
|
+
}
|
|
622
640
|
// Check autoprovision flag in order to register or not device
|
|
623
641
|
if (group.autoprovision === undefined || group.autoprovision === true) {
|
|
642
|
+
logger.debug(context, 'Registering autoprovision of Device %j for its conf %j', newDevice, group);
|
|
624
643
|
registerDevice(newDevice, function (error, device) {
|
|
625
644
|
callback(error, device, group);
|
|
626
645
|
});
|
|
@@ -359,7 +359,9 @@ function updateRegisterDeviceNgsi2(deviceObj, entityInfoUpdated, callback) {
|
|
|
359
359
|
if ('expressionLanguage' in newDevice && newDevice.expressionLanguage !== undefined) {
|
|
360
360
|
oldDevice.expressionLanguage = newDevice.expressionLanguage;
|
|
361
361
|
}
|
|
362
|
-
|
|
362
|
+
if ('apikey' in newDevice && newDevice.apikey !== undefined) {
|
|
363
|
+
oldDevice.explicitAttrs = newDevice.apikey;
|
|
364
|
+
}
|
|
363
365
|
oldDevice.endpoint = newDevice.endpoint || oldDevice.endpoint;
|
|
364
366
|
|
|
365
367
|
callback(null, oldDevice);
|