iotagent-node-lib 2.24.0 → 2.25.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 (32) hide show
  1. package/.github/workflows/ci.yml +6 -7
  2. package/CHANGES_NEXT_RELEASE +1 -0
  3. package/doc/advanced-topics.md +23 -1
  4. package/doc/apiary/iotagent.apib +5 -5
  5. package/doc/deprecated.md +10 -7
  6. package/doc/expressionLanguage.md +44 -34
  7. package/doc/getting-started.md +1 -1
  8. package/examples/TTOpen-service.json +1 -1
  9. package/lib/fiware-iotagent-lib.js +2 -1
  10. package/lib/jexlTranformsMap.js +12 -1
  11. package/lib/plugins/jexlParser.js +2 -2
  12. package/lib/services/devices/deviceService.js +19 -1
  13. package/lib/services/devices/devices-NGSI-v2.js +3 -1
  14. package/lib/services/northBound/restUtils.js +3 -5
  15. package/lib/templates/deviceGroup.json +1 -1
  16. package/package.json +2 -2
  17. package/test/unit/examples/deviceProvisioningRequests/provisionNewDeviceEmpty.json +43 -0
  18. package/test/unit/examples/mongoCollections/configurations.json +3 -3
  19. package/test/unit/expressions/jexlExpression-test.js +29 -8
  20. package/test/unit/general/deviceService-test.js +31 -29
  21. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +25 -24
  22. package/test/unit/mongodb/mongodb-group-registry-test.js +3 -3
  23. package/test/unit/mongodb/mongodb-registry-test.js +30 -21
  24. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin30.json +11 -0
  25. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +18 -4
  26. package/test/unit/ngsi-ld/general/deviceService-test.js +31 -29
  27. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +0 -3
  28. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityJexlExpressionPlugin1.json +22 -0
  29. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +21 -6
  30. package/test/unit/ngsiv2/general/deviceService-test.js +25 -23
  31. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +58 -0
  32. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +25 -6
@@ -14,10 +14,10 @@ jobs:
14
14
  steps:
15
15
  - name: Git checkout
16
16
  uses: actions/checkout@v2
17
- - name: Use Node.js 12.x
17
+ - name: Use Node.js 16.x
18
18
  uses: actions/setup-node@v1
19
19
  with:
20
- node-version: 12.x
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 12.x
34
+ - name: Use Node.js 16.x
35
35
  uses: actions/setup-node@v1
36
36
  with:
37
- node-version: 12.x
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 12.x'
80
+ - name: 'Test Coverage with Node.js 16.x'
82
81
  uses: actions/setup-node@v1
83
82
  with:
84
- node-version: 12.x
83
+ node-version: 16.x
85
84
  - run: |
86
85
  npm install
87
86
  npm run test:coverage
@@ -0,0 +1 @@
1
+
@@ -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
@@ -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
- |`cbroker` |Context Broker endpoint assigned to this service, it must be a real uri.|
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
- "cbroker": "http://127.0.0.1:1026",
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
- "cbroker": "http://127.0.0.1:1026",
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 (cbroker field is temporary mandatory).
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
- "cbroker": "http://127.0.0.1:1026",
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,15 @@ 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
20
22
 
21
- The use of Node.js v12 is highly recommended.
23
+ The use of Node.js v14 is highly recommended.
22
24
 
23
25
  ## Using old iotagent-node-lib versions
24
26
 
@@ -38,8 +40,9 @@ The following table provides information about the last iotagent-node-lib versio
38
40
 
39
41
  | **Removed feature** | **Last iotagent-node-lib version supporting feature** | **That version release date** |
40
42
  | ---------------------- | ----------------------------------------------------- | ----------------------------- |
41
- | NGSI v1 API | Not yet defined | Not yet defined |
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 | February 18th, 2021 |
43
+ | NGSI v1 API | 2.17.0 | August 30th, 2021 |
44
+ | Support to Node.js v4 | 2.8.1 | December 19th, 2018 |
45
+ | Support to Node.js v6 | 2.9.0 | May 22nd, 2019 |
46
+ | Support to Node.js v8 | 2.12.0 | April 7th, 2020 |
47
+ | Support to Node.js v10 | 2.15.0 | February 18th, 2021 |
48
+ | Support to Node.js v12 | 2.24.0 | September 2nd, 2022 |
@@ -75,7 +75,7 @@ can check the following example:
75
75
  "services": [
76
76
  {
77
77
  "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
78
- "cbroker": "http://orion:1026",
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 | 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) &vert; 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
- | mapper: (val, values, choices) | <code>choices[values.findIndex((target) &vert; target == val)]);</code> |
488
- | thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) &vert; (acc==0)&vert;&vert;acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
489
- | bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="&vert;"?parseInt(i)&vert;mask: op==="^"?parseInt(i)^mask:i)>>shf;</code> |
490
- | slice: (arr, init, end) | `arr.slice(init,end);` |
491
- | addset: (arr, x) | <code>{ return Array.from((new Set(arr)).add(x)) }</code> |
492
- | removeset: (arr, x) | <code>{ let s = new Set(arr); s.delete(x); return Array.from(s) }</code> |
493
- | touppercase: (val) | `String(val).toUpperCase()` |
494
- | tolowercase: (val) | `String(val).toLowerCase()` |
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) &vert; 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) &vert; target == val)]);</code> |
490
+ | thmapper: (val, values, choices) | <code>choices[values.reduce((acc,curr,i,arr) &vert; (acc==0)&vert;&vert;acc?acc:val<=curr?acc=i:acc=null,null)];</code> |
491
+ | bitwisemask: (i,mask,op,shf) | <code>(op==="&"?parseInt(i)&mask: op==="&vert;"?parseInt(i)&vert;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.
@@ -56,7 +56,7 @@ curl -iX POST \
56
56
  "services": [
57
57
  {
58
58
  "apikey": "4jggokgpepnvsb2uv4s40d59ov",
59
- "cbroker": "http://orion:1026",
59
+ "cbHost": "http://orion:1026",
60
60
  "entity_type": "Device",
61
61
  "resource": "/iot/d",
62
62
  }
@@ -3,7 +3,7 @@
3
3
  {
4
4
  "resource": "/iot/d",
5
5
  "apikey": "pipopi",
6
- "cbroker": "http://127.0.0.1:10026",
6
+ "cbHost": "http://127.0.0.1:10026",
7
7
  "entity_type": "ThinkingThing"
8
8
  }
9
9
  ]
@@ -363,7 +363,8 @@ exports.dataPlugins = {
363
363
  timestampProcess: require('./plugins/timestampProcessPlugin'),
364
364
  expressionTransformation: require('./plugins/expressionPlugin'),
365
365
  multiEntity: require('./plugins/multiEntity'),
366
- bidirectionalData: require('./plugins/bidirectionalData')
366
+ bidirectionalData: require('./plugins/bidirectionalData'),
367
+ utils: require('./plugins/pluginUtils')
367
368
  };
368
369
 
369
370
  exports.alarms = require('./services/common/alarmManagement');
@@ -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
- error = new errors.InvalidExpression(expression);
145
- throw error;
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(context, 'deviceData before merge with conf: %j', deviceData);
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) {
@@ -362,6 +369,9 @@ function registerDevice(deviceObj, callback) {
362
369
  if ('explicitAttrs' in deviceData && deviceData.explicitAttrs !== undefined) {
363
370
  deviceObj.explicitAttrs = deviceData.explicitAttrs;
364
371
  }
372
+ if ('apikey' in deviceData && deviceData.apikey !== undefined) {
373
+ deviceObj.apikey = deviceData.apikey;
374
+ }
365
375
  config.getRegistry().store(deviceObj, callback);
366
376
  }
367
377
  }
@@ -619,8 +629,16 @@ function findOrCreate(deviceId, group, callback) {
619
629
  if ('expressionLanguage' in group && group.expressionLanguage !== undefined) {
620
630
  newDevice.expressionLanguage = group.expressionLanguage;
621
631
  }
632
+ if (
633
+ (!('apikey' in newDevice) || newDevice.apikey === undefined) &&
634
+ 'apikey' in group &&
635
+ group.apikey !== undefined
636
+ ) {
637
+ newDevice.apikey = group.apikey;
638
+ }
622
639
  // Check autoprovision flag in order to register or not device
623
640
  if (group.autoprovision === undefined || group.autoprovision === true) {
641
+ logger.debug(context, 'Registering autoprovision of Device %j for its conf %j', newDevice, group);
624
642
  registerDevice(newDevice, function (error, device) {
625
643
  callback(error, device, group);
626
644
  });
@@ -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);
@@ -53,11 +53,9 @@ function checkMandatoryQueryParams(mandatoryAttributes, body, callback) {
53
53
  for (const p in mandatoryAttributes) {
54
54
  let found = false;
55
55
 
56
- for (const i in body) {
57
- if (body.hasOwnProperty(i)) {
58
- if (i === mandatoryAttributes[p]) {
59
- found = true;
60
- }
56
+ if (body.hasOwnProperty(mandatoryAttributes[p])) {
57
+ if (body[mandatoryAttributes[p]] !== '' && body[mandatoryAttributes[p]] !== null) {
58
+ found = true;
61
59
  }
62
60
  }
63
61
 
@@ -24,7 +24,7 @@
24
24
  "description": "token",
25
25
  "type": "string"
26
26
  },
27
- "cbroker": {
27
+ "cbHost": {
28
28
  "description": "uri for the context broker",
29
29
  "type": "string"
30
30
  },
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "iotagent-node-lib",
3
3
  "license": "AGPL-3.0-only",
4
4
  "description": "IoT Agent library to interface with NGSI Context Broker",
5
- "version": "2.24.0",
5
+ "version": "2.25.0",
6
6
  "homepage": "https://github.com/telefonicaid/iotagent-node-lib",
7
7
  "keywords": [
8
8
  "fiware",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "main": "lib/fiware-iotagent-lib",
25
25
  "engines": {
26
- "node": ">=12"
26
+ "node": ">=14"
27
27
  },
28
28
  "scripts": {
29
29
  "clean": "rm -rf package-lock.json && rm -rf node_modules && rm -rf coverage",
@@ -0,0 +1,43 @@
1
+ {
2
+ "devices": [
3
+ {
4
+ "device_id": "",
5
+ "protocol": "GENERIC_PROTO",
6
+ "entity_name": "TheFirstLight",
7
+ "entity_type": "TheLightType",
8
+ "timezone": "America/Santiago",
9
+ "endpoint": "http://fakedEndpoint:1234",
10
+ "transport": "MQTT",
11
+ "attributes": [
12
+ {
13
+ "name": "attr_name",
14
+ "type": "string"
15
+ }
16
+ ],
17
+ "lazy": [
18
+ {
19
+ "name": "luminance",
20
+ "type": "lumens"
21
+ }
22
+ ],
23
+ "static_attributes": [
24
+ {
25
+ "name": "hardcodedAttr",
26
+ "type": "hardcodedType",
27
+ "value": "hardcodedValue"
28
+ }
29
+ ],
30
+ "commands": [
31
+ {
32
+ "name": "commandAttr",
33
+ "type": "commandType"
34
+ }
35
+ ],
36
+ "internal_attributes": [
37
+ {
38
+ "customField": "customValue"
39
+ }
40
+ ]
41
+ }
42
+ ]
43
+ }
@@ -2,7 +2,7 @@
2
2
  {
3
3
  "apikey" : "7e5721f478220be21f41f4700e50be2",
4
4
  "token" : "e50b7781d5e2d39d577e7c15e4a21f470ed39d5777e7c15e2e2d39d5777e7c1e2",
5
- "cbroker" : "http://10.0.0.2:1026",
5
+ "cbHost" : "http://10.0.0.2:1026",
6
6
  "entity_type" : "device",
7
7
  "resource" : "/iot/d",
8
8
  "service" : "smart_gondor",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  {
12
12
  "apikey" : "gdb13gq3f8q4beuk263besr8eh9ri",
13
- "cbroker" : "http://10.0.0.2:1026",
13
+ "cbHost" : "http://10.0.0.2:1026",
14
14
  "entity_type" : "device",
15
15
  "resource" : "/iot/d",
16
16
  "service" : "dumb_mordor",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  {
27
27
  "apikey" : "hs4h2ke7oerfdvrexbyh54w2",
28
- "cbroker" : "http://10.0.0.2:1026",
28
+ "cbHost" : "http://10.0.0.2:1026",
29
29
  "entity_type" : "device",
30
30
  "resource" : "/iot/d",
31
31
  "service" : "demo",
@@ -234,6 +234,26 @@ describe('Jexl expression interpreter', function () {
234
234
  });
235
235
  });
236
236
 
237
+ describe('When an concatarr function is used with an array', function () {
238
+ it('should work on the expression value', function (done) {
239
+ expressionParser.parse('array|concatarr(array)', scope, function (error, result) {
240
+ should.not.exist(error);
241
+ result.should.deepEqual([1, 2, 1, 2]);
242
+ done();
243
+ });
244
+ });
245
+ });
246
+
247
+ describe('When an joinarrtostr function is used with an array', function () {
248
+ it('should work on the expression value', function (done) {
249
+ expressionParser.parse('array|joinarrtostr("_")', scope, function (error, result) {
250
+ should.not.exist(error);
251
+ result.should.equal('1_2');
252
+ done();
253
+ });
254
+ });
255
+ });
256
+
237
257
  describe('When a function is applied to an array', function () {
238
258
  it('should work on the expression value', function (done) {
239
259
  expressionParser.parse('array[1]+1', scope, function (error, result) {
@@ -313,14 +333,15 @@ describe('Jexl expression interpreter', function () {
313
333
 
314
334
  describe('When a JSON parse transformation is applied', function () {
315
335
  it('should work on the expression value', function (done) {
316
- expressionParser.parse('"{\\"name\\":\\"John\\",\\"surname\\":\\"Doe\\"}"|jsonparse', scope, function (
317
- error,
318
- result
319
- ) {
320
- should.not.exist(error);
321
- result.should.eql(scope.object);
322
- done();
323
- });
336
+ expressionParser.parse(
337
+ '"{\\"name\\":\\"John\\",\\"surname\\":\\"Doe\\"}"|jsonparse',
338
+ scope,
339
+ function (error, result) {
340
+ should.not.exist(error);
341
+ result.should.eql(scope.object);
342
+ done();
343
+ }
344
+ );
324
345
  });
325
346
  });
326
347