iotagent-node-lib 4.6.0 → 4.7.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 (56) hide show
  1. package/.github/workflows/ci.yml +0 -1
  2. package/Changelog +12 -0
  3. package/config.js +3 -1
  4. package/doc/admin.md +39 -5
  5. package/doc/api.md +67 -6
  6. package/doc/requirements.txt +1 -1
  7. package/lib/commonConfig.js +21 -2
  8. package/lib/model/Command.js +2 -2
  9. package/lib/model/Device.js +7 -3
  10. package/lib/model/Group.js +5 -3
  11. package/lib/model/dbConn.js +53 -112
  12. package/lib/services/commands/commandRegistryMongoDB.js +115 -75
  13. package/lib/services/common/iotManagerService.js +3 -1
  14. package/lib/services/devices/deviceRegistryMemory.js +36 -0
  15. package/lib/services/devices/deviceRegistryMongoDB.js +160 -87
  16. package/lib/services/devices/deviceService.js +33 -3
  17. package/lib/services/devices/devices-NGSI-v2.js +6 -1
  18. package/lib/services/groups/groupRegistryMongoDB.js +120 -83
  19. package/lib/services/ngsi/entities-NGSI-v2.js +14 -1
  20. package/lib/services/ngsi/ngsiService.js +32 -1
  21. package/lib/services/northBound/deviceProvisioningServer.js +12 -3
  22. package/lib/templates/updateDevice.json +12 -0
  23. package/lib/templates/updateDeviceLax.json +12 -0
  24. package/package.json +4 -4
  25. package/test/functional/config-test.js +3 -2
  26. package/test/functional/testUtils.js +2 -2
  27. package/test/unit/examples/groupProvisioningRequests/provisionFullGroup.json +1 -0
  28. package/test/unit/general/config-multi-core-test.js +1 -2
  29. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +5 -4
  30. package/test/unit/general/deviceService-test.js +6 -5
  31. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +6 -5
  32. package/test/unit/mongodb/mongodb-connectionoptions-test.js +7 -39
  33. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1 -2
  34. package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +1 -2
  35. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +8 -5
  36. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +42 -41
  37. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +11 -10
  38. package/test/unit/ngsiv2/general/deviceService-test.js +6 -5
  39. package/test/unit/ngsiv2/general/https-support-test.js +1 -1
  40. package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +4 -3
  41. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +6 -5
  42. package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +17 -16
  43. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +10 -18
  44. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +21 -20
  45. package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +8 -7
  46. package/test/unit/ngsiv2/plugins/alias-plugin_test.js +12 -11
  47. package/test/unit/ngsiv2/plugins/custom-plugin_test.js +3 -2
  48. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +28 -27
  49. package/test/unit/ngsiv2/provisioning/device-group-api-test.js +6 -4
  50. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -11
  51. package/test/unit/ngsiv2/provisioning/device-provisioning-configGroup-api_test.js +11 -10
  52. package/test/unit/ngsiv2/provisioning/device-registration_test.js +5 -4
  53. package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +6 -5
  54. package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -1
  55. package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +5 -4
  56. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +8 -7
@@ -51,7 +51,6 @@ jobs:
51
51
  strategy:
52
52
  matrix:
53
53
  node-version:
54
- - 14.x
55
54
  - 16.x
56
55
  - 18.x
57
56
  steps:
package/Changelog ADDED
@@ -0,0 +1,12 @@
1
+ 4.7.0 (February 3rd, 2025)
2
+
3
+ - Add: store (and recover) previous jexlctxt and make available in current jexlctxt (#1690)
4
+ - Add: option to force to use CB flow control with new API field useCBflowControl at group and device device level (#1420)
5
+ - Add: useCBflowControl config setting (IOTA_CB_FLOW_CONTROL env var) to set CB flow control behaviour at instance level (#1420)
6
+ - Add: allow remove last measure in device
7
+ - Add: store last measure in device (by id, apikey, service and subservice) and new API field storeLastMeasure at group and device levels (#1669)
8
+ - Add: storeLastMeasure config setting (IOTA_STORE_LAST_MEASURE env var) to set default store last measure behaviour at instance level (#1669)
9
+ - Fix: set polling and transport for autoprovisioned devices
10
+ - Upgrade express dep from 4.19.2 to 4.21.2
11
+ - Upgrade mongodb devdep from 4.17.1 to 4.17.2
12
+ - Upgrade mongoose dep from 5.13.20 to 8.9.5 (solving vulnerabilies CVE-2024-53900 and CVE-2025-23061) (#1674)
package/config.js CHANGED
@@ -77,7 +77,9 @@ var config = {
77
77
  providerUrl: 'http://192.168.56.1:4041',
78
78
  deviceRegistrationDuration: 'P1M',
79
79
  defaultType: 'Thing',
80
- expressLimit: '1Mb'
80
+ expressLimit: '1Mb',
81
+ useCBflowControl: false,
82
+ storeLastMeasure: false
81
83
  };
82
84
 
83
85
  module.exports = config;
package/doc/admin.md CHANGED
@@ -125,9 +125,9 @@ allowing the computer to interpret the rest of the data with more clarity and de
125
125
  ```
126
126
 
127
127
  Under mixed mode, **NGSI v2** payloads are used for context broker communications by default, but this payload may also
128
- be switched to **NGSI LD** at group or device provisioning time using the `ngsiVersion` field in the
129
- provisioning API. The `ngsiVersion` field switch may be added at either group or device level, with the device level
130
- overriding the group setting.
128
+ be switched to **NGSI LD** at group or device provisioning time using the `ngsiVersion` field in the provisioning API.
129
+ The `ngsiVersion` field switch may be added at either group or device level, with the device level overriding the group
130
+ setting.
131
131
 
132
132
  #### `server`
133
133
 
@@ -306,7 +306,8 @@ added `agentPath`:
306
306
 
307
307
  #### `types`
308
308
 
309
- This parameter includes additional groups configuration as described into the [Config group API](api.md#config-group-api) section.
309
+ This parameter includes additional groups configuration as described into the
310
+ [Config group API](api.md#config-group-api) section.
310
311
 
311
312
  #### `service`
312
313
 
@@ -415,7 +416,38 @@ IotAgents, as all Express applications that use the body-parser middleware, have
415
416
  size that the application will handle. This default limit for ioiotagnets are 1Mb. So, if your IotAgent receives a
416
417
  request with a body that exceeds this limit, the application will throw a “Error: Request entity too large”.
417
418
 
418
- The 1Mb default can be changed setting the `expressLimit` configuration parameter (or equivalente `IOTA_EXPRESS_LIMIT` environment variable).
419
+ The 1Mb default can be changed setting the `expressLimit` configuration parameter (or equivalente `IOTA_EXPRESS_LIMIT`
420
+ environment variable).
421
+
422
+ #### `storeLastMeasure`
423
+
424
+ If this flag is activated, last measure arrived to Device IoTAgent without be processed will be stored in Device under
425
+ `lastMeasure` field (composed of sub-fields `timestamp` and `measure` for the measure itself, in multi-measure format).
426
+ This flag is overwritten by `storeLastMeasure` flag in group or device. This flag is disabled by default.
427
+
428
+ For example in a device document stored in MongoDB will be extended with a subdocument named lastMeasure like this:
429
+
430
+ ```json
431
+ {
432
+ "lastMeasure": {
433
+ "timestamp": "2025-01-09T10:35:33.079Z",
434
+ "measure": [
435
+ [
436
+ {
437
+ "name": "level",
438
+ "type": "Text",
439
+ "value": 33
440
+ }
441
+ ]
442
+ ]
443
+ }
444
+ }
445
+ ```
446
+
447
+ #### `useCBflowControl`
448
+
449
+ If this flag is activated, when iotAgent invokes Context Broker will use [flowControl option](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/admin/perf_tuning.md#updates-flow-control-mechanism). This flag is overwritten by
450
+ `useCBflowControl` flag in group or device. This flag is disabled by default.
419
451
 
420
452
  ### Configuration using environment variables
421
453
 
@@ -479,6 +511,8 @@ overrides.
479
511
  | IOTA_DEFAULT_ENTITY_NAME_CONJUNCTION | `defaultEntityNameConjunction` |
480
512
  | IOTA_RELAX_TEMPLATE_VALIDATION | `relaxTemplateValidation` |
481
513
  | IOTA_EXPRESS_LIMIT | `expressLimit` |
514
+ | IOTA_STORE_LAST_MEASURE | `storeLastMeasure` |
515
+ | IOTA_CB_FLOW_CONTROL | `useCBflowControl` |
482
516
 
483
517
  Note:
484
518
 
package/doc/api.md CHANGED
@@ -12,6 +12,7 @@
12
12
  - [Special measures and attributes names](#special-measures-and-attributes-names)
13
13
  - [Device to NGSI Mapping](#device-to-ngsi-mapping)
14
14
  - [Device autoprovision and entity creation](#device-autoprovision-and-entity-creation)
15
+ - [Entity Name expression support](#entity-name-expression-support)
15
16
  - [Multientity support](#multientity-support)
16
17
  - [Metadata support](#metadata-support)
17
18
  - [NGSI LD data and metadata considerations](#ngsi-ld-data-and-metadata-considerations)
@@ -164,7 +165,7 @@ parameters defined at device level in database, the parameters are inherit from
164
165
 
165
166
  ### Uniqueness of groups and devices
166
167
 
167
- Group uniqueness is defined by the combination of: service, subservice, resource and apikey
168
+ Group uniqueness is defined by the combination of: resource and apikey. This is so because given a measure (identified by an apikey) there is no way to identify group related if apikey is not unique for all services and subservices.
168
169
 
169
170
  Device uniqueness is defined by the combination of: service, subservice, device_id and apikey. Note that several devices
170
171
  with the same device_id are allowed in the same service and subservice as long as their apikeys are different.
@@ -187,9 +188,9 @@ configured for a single device in the device provisioning, or it can be defined
187
188
 
188
189
  The entity type should be defined both in the group and in the device, but the entity name (entity ID) is not defined in
189
190
  the group. In that case, if there is no a existing device the same device ID, the entity name of the device generated
190
- will be a concatenation of the entity type and the device ID (I.E: `entityType:device_id`). It is possible to define the
191
- entity name as an expression, using the [Expression Language](#expression-language-support) through the `entityNameExp`
192
- attribute in the group.
191
+ will be a concatenation of the entity type and the device ID (I.E: `entityType:device_id`). If you need to generate the
192
+ entity name differently, it is possible to define an expression to generate it, using the parameter `entityNameExp` in
193
+ the group as described in the [Entity Name expression support](#entity-name-expression-support) section.
193
194
 
194
195
  It is also possible to configure how each of the measures obtained from the device is mapped to different attributes.
195
196
  The name and type of the attribute is configured by the user (globally for all the devices in the group or in a per
@@ -271,6 +272,59 @@ If for any reason you need the entity at CB before the first measure of the corr
271
272
  IOTAgent, you can create it in advance using the Context Broker
272
273
  [NGSI v2 API](https://github.com/telefonicaid/fiware-orion/blob/master/doc/manuals/orion-api.md).
273
274
 
275
+ ## Entity Name expression support
276
+
277
+ By default, the entity name used to persist the device measures in the Context Broker can be defined in the device
278
+ provisioning or, if the device is autoprovisioned, it is generated by the IoT Agent as a concatenation of the entity
279
+ type and the device ID. If you need to generate the entity name differently, it is possible to define an expression to
280
+ generate it, using the [Expression Language](#expression-language-support) through the `entityNameExp` field in the
281
+ group.
282
+
283
+ With this feature, the entity name can be generated dynamically based not only on the device ID and entity type, but
284
+ also on the measures reported by the device or any other context information. The `entityNameExp` field is only
285
+ available at the group level. **Important**: when using `entityNameExp`, the `entity_name` field in the device
286
+ provisioning is ignored. This means that the entity name used to store the device information in the Context Broker is
287
+ always generated by the `entityNameExp` expression. If you need to explicitly define the entity name for a particular
288
+ device, you can include a particular condition in the `entityNameExp` expression to handle that case (e.g.
289
+ `id == 'myDevice' ? 'myEntity' : entityType + ':' + id`).
290
+
291
+ The following example shows how to define an entity name expression:
292
+
293
+ ```json
294
+ {
295
+ "services": [
296
+ {
297
+ "resource": "/json",
298
+ "apikey": "801230BJKL23Y9090DSFL123HJK09H324HV8732",
299
+ "entity_type": "TemperatureSensor",
300
+ "entityNameExp": "id + '__' + sn",
301
+ "attributes": [
302
+ {
303
+ "object_id": "t",
304
+ "name": "temperature",
305
+ "type": "Number"
306
+ },
307
+ {
308
+ "object_id": "sn",
309
+ "name": "serialNumber",
310
+ "type": "Text"
311
+ }
312
+ ]
313
+ }
314
+ ]
315
+ }
316
+ ```
317
+
318
+ As defined above, the `entityNameExp` is `id + '__' + sn` and it will generate the entity name by concatenating the
319
+ device ID and the serial number reported by the device. For example, for a given measure with `id` equal to `dev123` and
320
+ `sn` equal to `ABCDEF`, the resulting entity name will be `dev123__ABCDEF`.
321
+
322
+ Note that, when using `entityNameExp`, the `entity_name` of the device provisioning is set to the result of the
323
+ expression the first time the device is created. If the expression is modified later, the `entity_name` of the device
324
+ provisioning will not be updated, but the value used to persist the device measures in the Context Broker will be the
325
+ result of the new expression. This can lead to a situation where the `entity_name` of the device provisioning and the
326
+ entity name used in the Context Broker are different.
327
+
274
328
  ## Multientity support
275
329
 
276
330
  The IOTA is able to persists measures coming from a single device to more than one entity, declaring the target entities
@@ -555,7 +609,8 @@ of expression in the IoT Agent are:
555
609
  - [Metadata](#expression-support-in-metadata)
556
610
  - Commands payload transformation (push and pull).
557
611
  - Auto provisioned devices entity name. It is configured at config Group level by setting the `entityNameExp`
558
- parameter. It defines an expression to generate the Entity Name for autoprovisioned devices.
612
+ parameter. It defines an expression to generate the Entity Name for autoprovisioned devices. More information in the
613
+ [Entity Name expression support](#entity-name-expression-support) section.
559
614
  - Dynamic `endpoint` definition. Configured at device level, it defines where the device listen for push http
560
615
  commands. It can be either a static value or an expression.
561
616
 
@@ -568,6 +623,7 @@ expression. In all cases the following data is available to all expressions:
568
623
  - `service`: device service (`Fiware-Service`)
569
624
  - `subservice`: device subservice (`Fiware-ServicePath`)
570
625
  - `staticAttributes`: static attributes defined in the device or config group
626
+ - `oldCtxt`: previous JEXL context (related to last processed measure)
571
627
 
572
628
  Additionally, for attribute expressions (`expression`, `entity_name`), `entityNameExp` and metadata expressions
573
629
  (`expression`) the following is available in the **context** used to evalute:
@@ -1715,13 +1771,15 @@ Config group is represented by a JSON object with the following fields:
1715
1771
  | `static_attributes` | ✓ | | | this attributes will be added to all the entities of this group 'as is', additional `metadata` is optional. |
1716
1772
  | `internal_attributes` | ✓ | | | optional section with free format, to allow specific IoT Agents to store information along with the devices in the Device Registry. |
1717
1773
  | `explicitAttrs` | ✓ | string or bool | ✓ | optional field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](#explicitly-defined-attributes-explicitattrs) |
1718
- | `entityNameExp` | ✓ | string | | optional field to allow use expressions to define entity name, instead default name, based on the device ID and the entity type (i.e. `<device_id>:<entity_type>`) |
1774
+ | `entityNameExp` | ✓ | string | | optional field to allow use expressions to define entity name, instead default name, based on the device ID and the entity type (i.e. `<device_id>:<entity_type>`). More information in [specific section](#entity-name-expression-entitynameexp) |
1719
1775
  | `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. |
1720
1776
  | `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. |
1721
1777
  | `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`. |
1722
1778
  | `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`. |
1723
1779
  | `transport` | ✓ | `string` | | Transport protocol used by the group of devices to send updates, for the IoT Agents with multiple transport protocols. |
1724
1780
  | `endpoint` | ✓ | `string` | | Endpoint where the group of device is going to receive commands, if any. |
1781
+ | `storeLastMeasure` | ✓ | `boolean` | | Store in device last measure received. See more info [in this section](admin.md#storelastmeasure). False by default |
1782
+ | `useCBflowControl` | ✓ | `boolean` | | Use Context Broker flow control. See more info [in this section](admin.md#useCBflowControl). False by default |
1725
1783
 
1726
1784
  ### Config group operations
1727
1785
 
@@ -1942,6 +2000,9 @@ the API resource fields and the same fields in the database model.
1942
2000
  | `explicitAttrs` | ✓ | `boolean` | ✓ | Field to support selective ignore of measures so that IOTA doesn’t progress. See details in [specific section](#explicitly-defined-attributes-explicitattrs) |
1943
2001
  | `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. |
1944
2002
  | `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`. |
2003
+ | `storeLastMeasure` | ✓ | `boolean` | | Store in device last measure received. Useful just for debugging purpose. See more info [in this section](admin.md#storelastmeasure). False by default. |
2004
+ | `lastMeasure` | ✓ | `object` | | last measure stored on device when `storeLastMeasure` is enabled. See more info [in this section](admin.md#storelastmeasure). This field can be cleared using `{}` in a device update request. In that case, `lastMeasure` is removed from device (until a next measure is received and `lastMesuare` gets created again). |
2005
+ | `useCBflowControl` | ✓ | `boolean` | | Use Context Broker flow control. See more info [in this section](admin.md#useCBflowControl). False by default. |
1945
2006
 
1946
2007
  ### Device operations
1947
2008
 
@@ -1,4 +1,4 @@
1
1
  mkdocs==1.2.4
2
2
  Pygments==2.15.0
3
3
  Markdown==3.3.4
4
- jinja2==3.1.4
4
+ jinja2==3.1.5
@@ -157,7 +157,9 @@ function processEnvironmentVariables() {
157
157
  'IOTA_FALLBACK_PATH',
158
158
  'IOTA_LD_SUPPORT_NULL',
159
159
  'IOTA_LD_SUPPORT_DATASET_ID',
160
- 'IOTA_EXPRESS_LIMIT'
160
+ 'IOTA_EXPRESS_LIMIT',
161
+ 'IOTA_USE_CB_FLOW_CONTROL',
162
+ 'IOTA_STORE_LAST_MEASURE'
161
163
  ];
162
164
  const iotamVariables = [
163
165
  'IOTA_IOTAM_URL',
@@ -350,6 +352,11 @@ function processEnvironmentVariables() {
350
352
  config.defaultResource = process.env.IOTA_DEFAULT_RESOURCE;
351
353
  }
352
354
 
355
+ // Default transport
356
+ if (process.env.IOTA_DEFAULT_TRANSPORT !== undefined) {
357
+ config.defaultTransport = process.env.IOTA_DEFAULT_TRANSPORT;
358
+ }
359
+
353
360
  // Default explicitAttrs
354
361
  if (process.env.IOTA_EXPLICIT_ATTRS !== undefined) {
355
362
  config.explicitAttrs = process.env.IOTA_EXPLICIT_ATTRS;
@@ -474,6 +481,16 @@ function processEnvironmentVariables() {
474
481
  } else {
475
482
  config.expressLimit = config.expressLimit ? config.expressLimit : '1mb';
476
483
  }
484
+ if (process.env.IOTA_USE_CB_FLOW_CONTROL) {
485
+ config.useCBflowControl = process.env.IOTA_USE_CB_FLOW_CONTROL === 'true';
486
+ } else {
487
+ config.useCBflowControl = config.useCBflowControl === true;
488
+ }
489
+ if (process.env.IOTA_STORE_LAST_MEASURE) {
490
+ config.storeLastMeasure = process.env.IOTA_STORE_LAST_MEASURE === 'true';
491
+ } else {
492
+ config.storeLastMeasure = config.storeLastMeasure === true;
493
+ }
477
494
  }
478
495
 
479
496
  function setConfig(newConfig) {
@@ -503,7 +520,9 @@ function getConfigForTypeInformation() {
503
520
  multiCore: config.multiCore,
504
521
  relaxTemplateValidation: config.relaxTemplateValidation,
505
522
  defaultEntityNameConjunction: config.defaultEntityNameConjunction,
506
- defaultType: config.defaultType
523
+ defaultType: config.defaultType,
524
+ useCBflowControl: config.useCBflowControl,
525
+ storeLastMeasure: config.storeLastMeasure
507
526
  };
508
527
  return conf;
509
528
  }
@@ -34,8 +34,8 @@ const Command = new Schema({
34
34
  creationDate: { type: Date, default: Date.now }
35
35
  });
36
36
 
37
- function load(db) {
38
- module.exports.model = db.model('Command', Command);
37
+ function load() {
38
+ module.exports.model = mongoose.model('Command', Command);
39
39
  module.exports.internalSchema = Command;
40
40
  }
41
41
 
@@ -53,11 +53,15 @@ const Device = new Schema({
53
53
  autoprovision: Boolean,
54
54
  explicitAttrs: Group.ExplicitAttrsType,
55
55
  ngsiVersion: String,
56
- payloadType: String
56
+ payloadType: String,
57
+ useCBflowControl: Boolean,
58
+ storeLastMeasure: Boolean,
59
+ lastMeasure: Object,
60
+ oldCtxt: Object
57
61
  });
58
62
 
59
- function load(db) {
60
- module.exports.model = db.model('Device', Device);
63
+ function load() {
64
+ module.exports.model = mongoose.model('Device', Device);
61
65
  module.exports.internalSchema = Device;
62
66
  }
63
67
 
@@ -65,12 +65,14 @@ const Group = new Schema({
65
65
  defaultEntityNameConjunction: String,
66
66
  ngsiVersion: String,
67
67
  entityNameExp: String,
68
- payloadType: String
68
+ payloadType: String,
69
+ useCBflowControl: Boolean,
70
+ storeLastMeasure: Boolean
69
71
  });
70
72
 
71
- function load(db) {
73
+ function load() {
72
74
  Group.index({ apikey: 1, resource: 1 }, { unique: true });
73
- module.exports.model = db.model('Group', Group);
75
+ module.exports.model = mongoose.model('Group', Group);
74
76
  module.exports.internalSchema = Group;
75
77
  }
76
78
 
@@ -31,7 +31,6 @@ const config = require('../commonConfig');
31
31
  const constants = require('../constants');
32
32
  const alarms = require('../services/common/alarmManagement');
33
33
  const logger = require('logops');
34
- const async = require('async');
35
34
  const errors = require('../errors');
36
35
  let defaultDb;
37
36
  const DEFAULT_DB_NAME = 'iotagent';
@@ -40,9 +39,9 @@ const context = {
40
39
  };
41
40
 
42
41
  function loadModels() {
43
- require('./Device').load(defaultDb);
44
- require('./Group').load(defaultDb);
45
- require('./Command').load(defaultDb);
42
+ require('./Device').load();
43
+ require('./Group').load();
44
+ require('./Command').load();
46
45
  }
47
46
 
48
47
  /**
@@ -50,93 +49,52 @@ function loadModels() {
50
49
  *
51
50
  * @this Reference to the dbConn module itself.
52
51
  */
52
+
53
53
  function init(host, db, port, options, callback) {
54
- /*jshint camelcase:false, validthis:true */
55
54
  let url;
56
55
  let retries = 0;
57
56
  let lastError;
58
- const maxRetries =
59
- (config.getConfig().mongodb && config.getConfig().mongodb.retries) || constants.DEFAULT_MONGODB_RETRIES;
57
+ const maxRetries = config.getConfig().mongodb?.retries || constants.DEFAULT_MONGODB_RETRIES;
60
58
 
61
59
  function addPort(item) {
62
- return item + ':' + port;
63
- }
64
-
65
- function commaConcat(previous, current, currentIndex) {
66
- if (currentIndex !== 0) {
67
- previous += ',';
68
- }
69
-
70
- previous += current;
71
-
72
- return previous;
60
+ return `${item}:${port}`;
73
61
  }
74
62
 
75
63
  url = 'mongodb://';
76
64
 
77
65
  if (options.auth) {
78
- url += options.auth.user + ':' + options.auth.password + '@';
66
+ url += `${encodeURIComponent(options.auth.user)}:${encodeURIComponent(options.auth.password)}@`;
79
67
  }
80
68
 
81
- const hosts = host.split(',').map(addPort).reduce(commaConcat, '');
82
-
83
- url += hosts + '/' + db;
69
+ const hosts = host.split(',').map(addPort).join(',');
70
+ url += `${hosts}/${db}`;
84
71
 
85
72
  if (options.extraArgs) {
86
- if (options.extraArgs instanceof Object && Object.keys(options.extraArgs).length > 0) {
87
- url += '?';
88
- url += Object.entries(options.extraArgs)
89
- .map(function ([k, v]) {
90
- return encodeURIComponent(k) + '=' + encodeURIComponent(v);
91
- })
92
- .join('&');
73
+ const query = new URLSearchParams(options.extraArgs).toString();
74
+ if (query) {
75
+ url += `?${query}`;
93
76
  }
94
77
  delete options.extraArgs;
95
78
  }
96
79
 
97
- /* eslint-disable-next-line no-unused-vars */
98
- function createConnectionHandler(error, results) {
99
- if (defaultDb) {
100
- logger.info(context, 'Successfully connected to MongoDB.');
101
- module.exports.db = defaultDb;
102
- loadModels();
103
- } else {
104
- logger.error(context, 'MONGODB-002: Error found after [%d] attempts: %s', retries, error || lastError);
105
- }
106
-
107
- callback(error);
108
- }
109
-
110
- function retryCheck() {
111
- return !defaultDb && retries < maxRetries;
112
- }
113
-
114
- function connectionAttempt(url, options, callback) {
115
- logger.info(context, 'Attempting to connect to MongoDB instance with url %j. Attempt %d', url, retries);
116
- // FIXME: useNewUrlParser is no longer used in underlying mongodb driver 4.x
117
- // (see https://github.com/mongodb/node-mongodb-native/blob/HEAD/etc/notes/CHANGES_4.0.0.md)
118
- // but not sure if current mongoose version is still using mongodb 3.x internally
119
- // probably mongodb-connectionoptions-test.js needs to be fixed if useNewUrlParser is removed at the end
120
- options.useNewUrlParser = true;
121
- options.useUnifiedTopology = true;
122
- mongoose.set('useCreateIndex', true);
123
- /* eslint-disable-next-line no-unused-vars */
124
- const candidateDb = mongoose.createConnection(url, options, function (error, result) {
125
- if (error) {
126
- logger.error(context, 'MONGODB-001: Error trying to connect to MongoDB: %s', error);
127
- lastError = error;
128
- } else {
129
- defaultDb = candidateDb;
80
+ function connectionAttempt(callback) {
81
+ logger.info(context, `Attempting to connect to MongoDB at ${url}. Attempt ${retries + 1}`);
130
82
 
83
+ mongoose
84
+ .connect(url, options)
85
+ .then(() => {
86
+ defaultDb = mongoose.connection;
87
+ logger.info(context, 'Successfully connected to MongoDB.');
88
+ loadModels();
131
89
  defaultDb.on('error', function (error) {
132
90
  logger.error(context, 'Mongo Driver error: %j', error);
91
+ lastError = error;
133
92
  alarms.raise(constants.MONGO_ALARM, error);
134
93
  });
135
94
  /* eslint-disable-next-line no-unused-vars */
136
95
  defaultDb.on('connecting', function (error) {
137
96
  logger.debug(context, 'Mongo Driver connecting');
138
97
  });
139
-
140
98
  defaultDb.on('connected', function () {
141
99
  logger.debug(context, 'Mongo Driver connected');
142
100
  });
@@ -159,69 +117,52 @@ function init(host, db, port, options, callback) {
159
117
  defaultDb.on('close', function () {
160
118
  logger.debug(context, 'Mongo Driver close');
161
119
  });
162
- }
163
-
164
- callback();
165
- });
166
- }
167
-
168
- function tryCreateConnection(callback) {
169
- const attempt = async.apply(connectionAttempt, url, options, callback);
170
- const seconds =
171
- (config.getConfig().mongodb && config.getConfig().mongodb.retryTime) ||
172
- constants.DEFAULT_MONGODB_RETRY_TIME;
173
-
174
- retries++;
175
-
176
- if (retries === 1) {
177
- logger.info(context, 'First connection attempt');
178
- attempt();
179
- } else {
180
- logger.info(context, 'Waiting %d seconds before attempting again.', seconds);
181
- setTimeout(attempt, seconds * 1000);
182
- }
120
+ callback();
121
+ })
122
+ .catch((err) => {
123
+ logger.error(context, `MONGODB-001: Error trying to connect to MongoDB: ${err}`);
124
+ lastError = err;
125
+ retries++;
126
+ if (retries < maxRetries) {
127
+ const retryTime = config.getConfig().mongodb?.retryTime || constants.DEFAULT_MONGODB_RETRY_TIME;
128
+ logger.info(context, `Retrying in ${retryTime} seconds...`);
129
+ setTimeout(() => connectionAttempt(callback), retryTime * 1000);
130
+ } else {
131
+ logger.error(
132
+ context,
133
+ 'MONGODB-002: Error to connect found after %d attempts: %s',
134
+ retries,
135
+ lastError
136
+ );
137
+ callback(err);
138
+ }
139
+ });
183
140
  }
184
141
 
185
- defaultDb = null;
186
- async.whilst(retryCheck, tryCreateConnection, createConnectionHandler);
142
+ connectionAttempt(callback);
187
143
  }
188
144
 
189
145
  function configureDb(callback) {
190
- /*jshint camelcase:false, validthis:true */
191
146
  const currentConfig = config.getConfig();
192
-
193
- if (currentConfig.deviceRegistry && currentConfig.deviceRegistry.type === 'mongodb') {
194
- if (!currentConfig.mongodb || !currentConfig.mongodb.host) {
147
+ if (currentConfig.deviceRegistry?.type === 'mongodb') {
148
+ if (!currentConfig.mongodb?.host) {
195
149
  logger.fatal(context, 'MONGODB-003: No host found for MongoDB driver.');
196
150
  callback(new errors.BadConfiguration('No host found for MongoDB driver'));
197
151
  } else {
198
- let dbName = currentConfig.mongodb.db;
152
+ const dbName = currentConfig.mongodb.db || DEFAULT_DB_NAME;
199
153
  const port = currentConfig.mongodb.port || 27017;
200
154
  const options = {};
201
155
 
202
- if (!currentConfig.mongodb.db) {
203
- dbName = DEFAULT_DB_NAME;
204
- }
205
-
206
- if (currentConfig.mongodb.replicaSet) {
207
- options.replicaSet = currentConfig.mongodb.replicaSet;
208
- }
209
-
210
- if (currentConfig.mongodb.ssl) {
211
- options.ssl = currentConfig.mongodb.ssl;
212
- }
213
-
214
- if (currentConfig.mongodb.extraArgs) {
215
- options.extraArgs = currentConfig.mongodb.extraArgs;
216
- }
156
+ if (currentConfig.mongodb.replicaSet) options.replicaSet = currentConfig.mongodb.replicaSet;
157
+ if (currentConfig.mongodb.ssl) options.ssl = currentConfig.mongodb.ssl;
158
+ if (currentConfig.mongodb.extraArgs) options.extraArgs = currentConfig.mongodb.extraArgs;
217
159
 
218
160
  if (currentConfig.mongodb.user && currentConfig.mongodb.password) {
219
- options.auth = {};
220
- options.auth.user = currentConfig.mongodb.user;
221
- options.auth.password = currentConfig.mongodb.password;
222
- // authSource only applies if auth is set
161
+ options.auth = {
162
+ user: currentConfig.mongodb.user,
163
+ password: currentConfig.mongodb.password
164
+ };
223
165
  if (currentConfig.mongodb.authSource) {
224
- // Overload extraArgs if it was set
225
166
  options.extraArgs = {
226
167
  ...options.extraArgs,
227
168
  authSource: currentConfig.mongodb.authSource
@@ -229,7 +170,7 @@ function configureDb(callback) {
229
170
  }
230
171
  }
231
172
 
232
- init(config.getConfig().mongodb.host, dbName, port, options, callback);
173
+ init(currentConfig.mongodb.host, dbName, port, options, callback);
233
174
  }
234
175
  } else {
235
176
  callback();