iotagent-node-lib 3.3.0 → 3.4.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 (79) hide show
  1. package/README.md +10 -11
  2. package/doc/README.md +16 -0
  3. package/doc/admin.md +565 -0
  4. package/doc/api.md +9 -5
  5. package/doc/deprecated.md +16 -14
  6. package/doc/{architecture.md → devel/architecture.md} +3 -3
  7. package/doc/{Contribution.md → devel/contribution-guidelines.md} +43 -35
  8. package/doc/devel/development.md +1879 -0
  9. package/doc/{northboundinteractions.md → devel/northboundinteractions.md} +18 -33
  10. package/doc/index.md +3 -5
  11. package/docker/Mosquitto/README.md +1 -0
  12. package/lib/commonConfig.js +0 -5
  13. package/lib/fiware-iotagent-lib.js +1 -1
  14. package/lib/jexlTranformsMap.js +2 -1
  15. package/lib/request-shim.js +2 -2
  16. package/lib/services/commands/commandService.js +1 -1
  17. package/lib/services/common/genericMiddleware.js +1 -1
  18. package/lib/services/devices/deviceRegistryMemory.js +2 -2
  19. package/lib/services/devices/deviceRegistryMongoDB.js +22 -9
  20. package/lib/services/devices/deviceService.js +36 -30
  21. package/lib/services/devices/devices-NGSI-LD.js +14 -2
  22. package/lib/services/devices/devices-NGSI-mixed.js +0 -2
  23. package/lib/services/devices/devices-NGSI-v2.js +22 -100
  24. package/lib/services/groups/groupService.js +1 -1
  25. package/lib/services/ngsi/entities-NGSI-v2.js +11 -25
  26. package/lib/services/northBound/deviceProvisioningServer.js +14 -5
  27. package/mkdocs.yml +6 -11
  28. package/package.json +3 -3
  29. package/scripts/legacy_expression_tool/README.md +56 -38
  30. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +2 -2
  31. package/test/unit/mongodb/mongodb-registry-test.js +1 -1
  32. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +66 -65
  33. package/test/unit/ngsi-ld/general/https-support-test.js +1 -1
  34. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +8 -7
  35. package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +12 -11
  36. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +41 -39
  37. package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +122 -122
  38. package/test/unit/ngsi-ld/provisioning/device-registration_test.js +28 -28
  39. package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +18 -17
  40. package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +7 -7
  41. package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +8 -7
  42. package/test/unit/ngsiv2/examples/contextRequests/updateContext6.json +2 -0
  43. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin1.json +0 -12
  44. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin11.json +0 -4
  45. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin12.json +1 -5
  46. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin2.json +0 -12
  47. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin29.json +0 -12
  48. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin3.json +0 -4
  49. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +0 -10
  50. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin5.json +0 -4
  51. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin6.json +0 -4
  52. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin7.json +0 -4
  53. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin8.json +0 -12
  54. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin9.json +0 -4
  55. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +1 -5
  56. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +13 -12
  57. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
  58. package/test/unit/ngsiv2/general/https-support-test.js +1 -1
  59. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +3 -8
  60. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +10 -10
  61. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +8 -103
  62. package/test/unit/ngsiv2/provisioning/device-registration_test.js +8 -6
  63. package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +2 -1
  64. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +0 -1
  65. package/.nyc_output/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +0 -1
  66. package/.nyc_output/processinfo/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +0 -1
  67. package/.nyc_output/processinfo/index.json +0 -1
  68. package/doc/config-basic-example.js +0 -20
  69. package/doc/development.md +0 -285
  70. package/doc/howto.md +0 -641
  71. package/doc/installationguide.md +0 -365
  72. package/doc/operations.md +0 -127
  73. package/doc/usermanual.md +0 -900
  74. package/lib/plugins/bidirectionalData.js +0 -356
  75. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +0 -697
  76. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +0 -536
  77. /package/doc/{NorthboundInteractions.postman_collection → devel/NorthboundInteractions.postman_collection} +0 -0
  78. /package/doc/{echo.js → devel/echo.js} +0 -0
  79. /package/doc/{finalResult.js → devel/finalResult.js} +0 -0
@@ -145,7 +145,7 @@ This **NGSI-v2** payload is associated to an update operation (POST `/v2/op/upda
145
145
  }
146
146
  }
147
147
  ],
148
- "actionType": "update"
148
+ "actionType": "append"
149
149
  }
150
150
  ```
151
151
 
@@ -155,9 +155,8 @@ As it can be seen in the example, the payload is a JSON Object with the followin
155
155
  with the information needed to identify the target entity `id` and `type` attributes. The `entities` attribute is an
156
156
  array, so a single update context batch operation can be used to update multiple devices
157
157
 
158
- - An `actionType` indicating the type of update: if this attribute has the value `"append"` the appropriate entity and
159
- attributes will be created if the don't exist; if the value is `"update"`, an error will be thrown if the target
160
- resources don't exist.
158
+ - An `actionType` indicating the type of update. It has the value `"append"` the appropriate entity and attributes
159
+ will be created if the don't exist.
161
160
 
162
161
  The equivalent **NGSI-LD** payload is associated to an update operation (PATCH `/ngsi-ld/v1/entities/<entity>/attrs/`).
163
162
 
@@ -427,7 +426,7 @@ Be sure to understand how each scenario works (as shown in the theory section) b
427
426
  Along this document, IP addresses and passwords will be concealed. Substitute the concealed passwords by your own.
428
427
 
429
428
  A postman collection is available alongside this document to help in reproducing this examples. It can be found
430
- [here](./doc/NorthboundInteractions.postman_collection).
429
+ [here](NorthboundInteractions.postman_collection).
431
430
 
432
431
  ### Retrieving a token
433
432
 
@@ -507,8 +506,8 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
507
506
  }
508
507
  }
509
508
  ],
510
- "actionType": "update"
511
- } ' "https://<platform-ip>:10027/v2/op/update"
509
+ "actionType": "append"
510
+ } ' "https://<platform-ip>:1026/v2/op/update"
512
511
  ```
513
512
 
514
513
  If the request is correct, the Context Broker will reply with the following R1 response (200 OK):
@@ -554,7 +553,7 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
554
553
  }
555
554
  ],
556
555
  "attrs": ["temperature","pressure"]
557
- }' "https://<platform-ip>:10027/v2/op/query"
556
+ }' "https://<platform-ip>:1026/v2/op/query"
558
557
  ```
559
558
 
560
559
  The Context Broker will reply with the updated data values in R2 format (200 OK):
@@ -600,20 +599,6 @@ It is worth mentioning that the Context Broker will reply with a 200 OK status c
600
599
  refer to transport protocol level errors, while the status codes inside of a payload give information about the
601
600
  application level protocol.
602
601
 
603
- The example shows an error updating an non-existent attribute (due to the use of UPDATE instead of APPEND).
604
-
605
- The following error payload is also valid in standard NGSI:
606
-
607
- ```json
608
- {
609
- "error": "NotFound",
610
- "description": "The requested entity has not been found. Check type and id"
611
- }
612
- ```
613
-
614
- Different kinds of errors can return their information in different formats, so NGSI implementations should check for
615
- the existence of both.
616
-
617
602
  ### Scenario 2: lazy attributes (happy path)
618
603
 
619
604
  Scenario 2 relies on the Context Provider mechanism of the Context Broker. For this scenario to work, the IoTAgent must
@@ -639,7 +624,7 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
639
624
  }
640
625
  }
641
626
  }
642
- ' "https://<platform-ip>:10027/v2/registrations"
627
+ ' "https://<platform-ip>:1026/v2/registrations"
643
628
  ```
644
629
 
645
630
  If everything has gone OK, the Context Broker will return the following payload:
@@ -669,7 +654,7 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
669
654
  }
670
655
  ],
671
656
  "attrs": ["batteryLevel"]
672
- }' "https://<platform-ip>:10027/v2/op/query"
657
+ }' "https://<platform-ip>:1026/v2/op/query"
673
658
  ```
674
659
 
675
660
  The Context Broker receives this request and detects that it can be served by a Context Provider (the IoT Agent), so it
@@ -772,7 +757,7 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
772
757
  }
773
758
  ],
774
759
  "duration": "P1M"
775
- }' "https://<platform-ip>:10027/v2/registrations"
760
+ }' "https://<platform-ip>:1026/v2/registrations"
776
761
  ```
777
762
 
778
763
  If everything has gone OK, the Context Broker will return the following payload:
@@ -806,8 +791,8 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
806
791
  }
807
792
  }
808
793
  ],
809
- "updateAction": "update"
810
- } ' "https://<platform-ip>:10027/v2/op/update"
794
+ "updateAction": "append"
795
+ } ' "https://<platform-ip>:1026/v2/op/update"
811
796
  ```
812
797
 
813
798
  The Context Broker receives this command and detects that it can be served by a Context Provider (the IoT Agent), so it
@@ -836,7 +821,7 @@ Fiware-Correlator: 9cae9496-8ec7-11e6-80fc-fa163e734aab
836
821
  }
837
822
  }
838
823
  ],
839
- "updateAction" : "update"
824
+ "updateAction" : "append"
840
825
  }
841
826
  ```
842
827
 
@@ -900,8 +885,8 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
900
885
  }
901
886
  }
902
887
  ],
903
- "actionType": "update"
904
- } ' "https://<platform-ip>:10027/v2/op/update"
888
+ "actionType": "append"
889
+ } ' "https://<platform-ip>:1026/v2/op/update"
905
890
  ```
906
891
 
907
892
  This update does not modify the original command attribute, but two auxiliary attributes, that are not provided by the
@@ -948,7 +933,7 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
948
933
  "switch_info",
949
934
  "switch_status"
950
935
  ]
951
- }' "https://<platform-ip>:10027/v2/op/query"
936
+ }' "https://<platform-ip>:1026/v2/op/query"
952
937
  ```
953
938
 
954
939
  The Context Broker replies with all the desired data, in R2 format (200 OK):
@@ -994,8 +979,8 @@ curl -X POST -H "Content-Type: application/json" -H "Accept: application/json" -
994
979
  }
995
980
  }
996
981
  ],
997
- "actionType": "update"
998
- } ' "https://<platform-ip>:10027/v2/op/update"
982
+ "actionType": "append"
983
+ } ' "https://<platform-ip>:1026/v2/op/update"
999
984
  ```
1000
985
 
1001
986
  In this case, the Context Broker reply with the following response (200 OK):
package/doc/index.md CHANGED
@@ -11,8 +11,8 @@ Broker using their own native protocols. IoT Agents should also be able to deal
11
11
  platform (authentication and authorization of the channel) and provide other common services to the device programmer.
12
12
 
13
13
  Github's [README.md](https://github.com/telefonicaid/iotagent-node-lib/blob/master/README.md) provides a good
14
- documentation summary. The [User Manual](usermanual.md) and the [Admin Guide](installationguide.md) cover more advanced
15
- topics.
14
+ documentation summary. The [API reference](doc/api.md) and the [Development documentation](devel/development.md) cover
15
+ more advanced topics.
16
16
 
17
17
  ## Background
18
18
 
@@ -26,8 +26,6 @@ functions.
26
26
  communications are left to the library.
27
27
  - Standardized OAuth2-based security is available to enable each IoT Agent to connect to several common Identity
28
28
  Managers (e.g. Keystone and Keyrock) so that communications can be restricted to trusted components.
29
- - A series of additional plugins are offered where necessary to allow for expression parsing, attribute aliasing and
30
- the processing of timestamp metadata.
31
29
 
32
30
  Each individual IoT Agent offers is driven by a `config.js` configuration file contains explicit custom settings based
33
31
  on the protocol and payload the IoT Agent is translating. It will also contain some common flags for common
@@ -51,5 +49,5 @@ IoT Agent
51
49
  In order to use the library within your own IoT Agent, you must first you require it before use:
52
50
 
53
51
  ```javascript
54
- const iotagentLib = require("iotagent-node-lib");
52
+ const iotagentLib = require('iotagent-node-lib');
55
53
  ```
@@ -2,6 +2,7 @@ Thi directory containts the Dockerfile (and associated files) for a container of
2
2
  This container is provide as a help for users to test with MQTT, but it is just an auxiliary material in this repository.
3
3
 
4
4
  The following releases matches with eclipse-mosquitto version:
5
+ - 2.1.0 uses mosquitto-2.0.11 from Debian 12
5
6
  - 2.0.0 uses mosquitto-2.0.11 from Debian 11
6
7
  - 1.6.0 uses mosquitto-1.6.10-1.el7.x86_64 (from Centos7)
7
8
  - 1.5.0 uses mosquitto-1.6.10-1.el7.x86_64 (from Centos7)
@@ -149,7 +149,6 @@ function processEnvironmentVariables() {
149
149
  'IOTA_MONGO_USER',
150
150
  'IOTA_MONGO_RETRY_TIME',
151
151
  'IOTA_SINGLE_MODE',
152
- 'IOTA_APPEND_MODE',
153
152
  'IOTA_POLLING_EXPIRATION',
154
153
  'IOTA_POLLING_DAEMON_FREQ',
155
154
  'IOTA_MULTI_CORE',
@@ -450,10 +449,6 @@ function processEnvironmentVariables() {
450
449
  config.singleConfigurationMode = process.env.IOTA_SINGLE_MODE === 'true';
451
450
  }
452
451
 
453
- if (process.env.IOTA_APPEND_MODE) {
454
- config.appendMode = process.env.IOTA_APPEND_MODE === 'true';
455
- }
456
-
457
452
  if (process.env.IOTA_POLLING_EXPIRATION) {
458
453
  config.pollingExpiration = process.env.IOTA_POLLING_EXPIRATION;
459
454
  }
@@ -302,6 +302,7 @@ exports.update = ngsi.update;
302
302
  exports.setCommandResult = ngsi.setCommandResult;
303
303
  exports.listDevices = deviceService.listDevices;
304
304
  exports.getDevice = deviceService.getDevice;
305
+ exports.updateDevice = deviceService.updateDevice;
305
306
  exports.getDeviceSilently = deviceService.getDeviceSilently;
306
307
  exports.getDeviceByName = deviceService.getDeviceByName;
307
308
  exports.getDeviceByNameAndType = deviceService.getDeviceByNameAndType;
@@ -349,7 +350,6 @@ exports.middlewares = middlewares;
349
350
 
350
351
  exports.dataPlugins = {
351
352
  expressionTransformation: require('./plugins/expressionPlugin'),
352
- bidirectionalData: require('./plugins/bidirectionalData'),
353
353
  utils: require('./plugins/pluginUtils')
354
354
  };
355
355
 
@@ -79,7 +79,8 @@ const map = {
79
79
  // https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleString
80
80
  localestring: (d, timezone, options) => new Date(d).toLocaleString(timezone, options),
81
81
  now: () => Date.now(),
82
- hextostring: (val) => new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map(byte => parseInt(byte, 16))))
82
+ hextostring: (val) =>
83
+ new TextDecoder().decode(new Uint8Array(val.match(/.{1,2}/g).map((byte) => parseInt(byte, 16))))
83
84
  };
84
85
 
85
86
  exports.map = map;
@@ -59,7 +59,7 @@ function getOptions(options) {
59
59
  // got library is not properly documented, so it is not clear which takes precedence
60
60
  // among body, json and form (see https://stackoverflow.com/q/70754880/1485926).
61
61
  // Thus, we are enforcing our own precedence with the "else if" chain below.
62
- // Behaviour is consistent with the one described at usermanual.md#iotagentlibrequest
62
+ // Behaviour is consistent with the one described at development.md#iotagentlibrequest
63
63
 
64
64
  if (options.method === 'GET' || options.method === 'HEAD' || options.method === 'OPTIONS') {
65
65
  // Do nothing - Never add a body
@@ -70,7 +70,7 @@ function getOptions(options) {
70
70
  // json takes precedence over form
71
71
  httpOptions.json = options.json;
72
72
  } else if (options.form) {
73
- // Note that we don't consider 'form' part of the function API (check usermanual.md#iotagentlibrequest)
73
+ // Note that we don't consider 'form' part of the function API (check development.md#iotagentlibrequest)
74
74
  // but we are preparing the code anyway as a safe measure
75
75
  httpOptions.form = options.form;
76
76
  }
@@ -120,7 +120,7 @@ function markAsExpired(command) {
120
120
 
121
121
  async.waterfall(
122
122
  [
123
- apply(deviceService.getDevice, command.deviceId, command.service, command.subservice),
123
+ apply(deviceService.getDevice, command.deviceId, null, command.service, command.subservice),
124
124
  getGroup,
125
125
  calculateTypeInformation,
126
126
  updateExpiredCommand
@@ -56,7 +56,7 @@ function handleError(error, req, res, next) {
56
56
  * Express middleware for tracing the complete request arriving to the IoTA in debug mode.
57
57
  */
58
58
  function traceRequest(req, res, next) {
59
- logger.debug(context, 'Request for path [%s] from [%s]', req.path, req.get('host'));
59
+ logger.debug(context, 'Request for path [%s] query [%j] from [%s]', req.path, req.query, req.get('host'));
60
60
 
61
61
  if (req.is('json') || req.is('application/ld+json')) {
62
62
  logger.debug(context, 'Body:\n\n%s\n\n', JSON.stringify(req.body, null, 4));
@@ -71,7 +71,7 @@ function storeDevice(newDevice, callback) {
71
71
  * @param {String} service Service of the device to remove.
72
72
  * @param {String} subservice Subservice inside the service for the removed device.
73
73
  */
74
- function removeDevice(id, service, subservice, callback) {
74
+ function removeDevice(id, apikey, service, subservice, callback) {
75
75
  const services = Object.keys(registeredDevices);
76
76
 
77
77
  for (let i = 0; i < services.length; i++) {
@@ -141,7 +141,7 @@ function listDevices(type, service, subservice, limit, offset, callback) {
141
141
  });
142
142
  }
143
143
 
144
- function getDevice(id, service, subservice, callback) {
144
+ function getDevice(id, apikey, service, subservice, callback) {
145
145
  if (registeredDevices[service] && registeredDevices[service][id]) {
146
146
  callback(null, registeredDevices[service][id]);
147
147
  } else {
@@ -119,13 +119,15 @@ function storeDevice(newDevice, callback) {
119
119
  * @param {String} service Service of the device to remove.
120
120
  * @param {String} subservice Subservice inside the service for the removed device.
121
121
  */
122
- function removeDevice(id, service, subservice, callback) {
122
+ function removeDevice(id, apikey, service, subservice, callback) {
123
123
  const condition = {
124
124
  id,
125
125
  service,
126
126
  subservice
127
127
  };
128
-
128
+ if (apikey) {
129
+ condition.apikey = apikey;
130
+ }
129
131
  logger.debug(context, 'Removing device with id [%s]', id);
130
132
 
131
133
  Device.model.deleteOne(condition, function (error) {
@@ -211,17 +213,21 @@ function findOneInMongoDB(queryParams, id, callback) {
211
213
  * Internal function used to find a device in the DB.
212
214
  *
213
215
  * @param {String} id ID of the Device to find.
216
+ * @param {String} Apikey Apikey of the Device to find.
214
217
  * @param {String} service Service the device belongs to (optional).
215
218
  * @param {String} subservice Division inside the service (optional).
216
219
  */
217
- function getDeviceById(id, service, subservice, callback) {
218
- const queryParams = {
220
+ function getDeviceById(id, apikey, service, subservice, callback) {
221
+ let queryParams = {
219
222
  id,
220
223
  service,
221
224
  subservice
222
225
  };
226
+ if (apikey) {
227
+ queryParams.apikey = apikey;
228
+ }
223
229
  context = fillService(context, queryParams);
224
- logger.debug(context, 'Looking for device with id [%s].', id);
230
+ logger.debug(context, 'Looking for device with id [%s] and queryParams [%j].', id, queryParams);
225
231
  findOneInMongoDB(queryParams, id, callback);
226
232
  }
227
233
 
@@ -232,10 +238,17 @@ function getDeviceById(id, service, subservice, callback) {
232
238
  * @param {String} service Service the device belongs to.
233
239
  * @param {String} subservice Division inside the service.
234
240
  */
235
- function getDevice(id, service, subservice, callback) {
236
- getDeviceById(id, service, subservice, function (error, data) {
241
+ function getDevice(id, apikey, service, subservice, callback) {
242
+ getDeviceById(id, apikey, service, subservice, function (error, data) {
237
243
  if (error) {
238
- callback(error);
244
+ // Try without apikey: apikey will be added to device later
245
+ getDeviceById(id, null, service, subservice, function (error, data) {
246
+ if (error) {
247
+ callback(error);
248
+ } else {
249
+ callback(null, data);
250
+ }
251
+ });
239
252
  } else {
240
253
  callback(null, data);
241
254
  }
@@ -284,7 +297,7 @@ function getByName(name, service, servicepath, callback) {
284
297
  */
285
298
  function update(device, callback) {
286
299
  logger.debug(context, 'Storing updated values for device [%s]:\n%s', device.id, JSON.stringify(device, null, 4));
287
- getDeviceById(device.id, device.service, device.subservice, function (error, data) {
300
+ getDevice(device.id, device.apikey, device.service, device.subservice, function (error, data) {
288
301
  if (error) {
289
302
  callback(error);
290
303
  } else {
@@ -64,22 +64,6 @@ function init() {
64
64
  }
65
65
  }
66
66
 
67
- /**
68
- * Creates the initial entity representing the device in the Context Broker. This is important mainly to allow the
69
- * rest of the updateContext operations to be performed using an UPDATE action instead of an APPEND one.
70
- *
71
- * @param {Object} deviceData Object containing all the deviceData needed to send the registration.
72
- * @param {Object} newDevice Device object that will be stored in the database.
73
- */
74
- function createInitialEntity(deviceData, newDevice, callback) {
75
- if (config.getConfig().appendMode === false || config.ngsiVersion() === 'ld' || deviceData.ngsiVersion === 'ld') {
76
- deviceHandler.createInitialEntity(deviceData, newDevice, callback);
77
- } else {
78
- logger.debug(context, 'Skip create initial entity due appendMode is false or ngsiLD.');
79
- callback(null, newDevice);
80
- }
81
- }
82
-
83
67
  /**
84
68
  * If the object_id or the name of the attribute is missing, complete it with the other piece of data.
85
69
  *
@@ -253,6 +237,7 @@ function registerDevice(deviceObj, callback) {
253
237
  function checkDuplicates(deviceObj, innerCb) {
254
238
  config.getRegistry().getSilently(
255
239
  deviceObj.id,
240
+ deviceObj.apikey,
256
241
  deviceObj.service,
257
242
  deviceObj.subservice,
258
243
  /* eslint-disable-next-line no-unused-vars */
@@ -354,8 +339,7 @@ function registerDevice(deviceObj, callback) {
354
339
  async.waterfall(
355
340
  [
356
341
  apply(registrationUtils.sendRegistrations, false, deviceData),
357
- apply(registrationUtils.processContextRegistration, deviceData),
358
- apply(createInitialEntity, deviceData)
342
+ apply(registrationUtils.processContextRegistration, deviceData)
359
343
  ],
360
344
  function (error, results) {
361
345
  if (error) {
@@ -419,7 +403,7 @@ function removeAllSubscriptions(device, callback) {
419
403
  * @param {String} service Service of the device to unregister.
420
404
  * @param {String} subservice Subservice inside the service for the unregisterd device.
421
405
  */
422
- function unregisterDevice(id, service, subservice, callback) {
406
+ function unregisterDevice(id, apikey, service, subservice, callback) {
423
407
  function processContextUnregister(body, innerCallback) {
424
408
  innerCallback(null);
425
409
  }
@@ -428,9 +412,9 @@ function unregisterDevice(id, service, subservice, callback) {
428
412
  innerCallback(null);
429
413
  }
430
414
 
431
- logger.debug(context, 'Removing device register in Device Service');
415
+ logger.debug(context, 'Removing device %j %j %j %j register in Device Service', id, apikey, service, subservice);
432
416
 
433
- config.getRegistry().get(id, service, subservice, function (error, device) {
417
+ config.getRegistry().get(id, apikey, service, subservice, function (error, device) {
434
418
  if (error) {
435
419
  callback(error);
436
420
  } else {
@@ -454,7 +438,7 @@ function unregisterDevice(id, service, subservice, callback) {
454
438
  processUnsubscribes,
455
439
  apply(registrationUtils.sendRegistrations, true, mergedDevice),
456
440
  processContextUnregister,
457
- apply(config.getRegistry().remove, id, service, subservice)
441
+ apply(config.getRegistry().remove, id, apikey, service, subservice)
458
442
  ],
459
443
  callback
460
444
  );
@@ -529,8 +513,17 @@ function listDevices(service, subservice, limit, offset, callback) {
529
513
  * @param {String} service Service for which the requested device.
530
514
  * @param {String} subservice Subservice inside the service for which the device is requested.
531
515
  */
532
- function getDevice(deviceId, service, subservice, callback) {
533
- config.getRegistry().get(deviceId, service, subservice, callback);
516
+ function getDevice(deviceId, apikey, service, subservice, callback) {
517
+ config.getRegistry().get(deviceId, apikey, service, subservice, callback);
518
+ }
519
+
520
+ /**
521
+ * Update a device from the device registry.
522
+ *
523
+ * @param {String} device JSON object contain the device to update.
524
+ */
525
+ function updateDevice(device, callback) {
526
+ config.getRegistry().update(device, callback);
534
527
  }
535
528
 
536
529
  /**
@@ -540,8 +533,8 @@ function getDevice(deviceId, service, subservice, callback) {
540
533
  * @param {String} service Service for which the requested device.
541
534
  * @param {String} subservice Subservice inside the service for which the device is requested.
542
535
  */
543
- function getDeviceSilently(deviceId, service, subservice, callback) {
544
- config.getRegistry().getSilently(deviceId, service, subservice, callback);
536
+ function getDeviceSilently(deviceId, apikey, service, subservice, callback) {
537
+ config.getRegistry().getSilently(deviceId, apikey, service, subservice, callback);
545
538
  }
546
539
 
547
540
  /**
@@ -608,10 +601,22 @@ function checkRegistry(fn) {
608
601
  };
609
602
  }
610
603
 
611
- function findOrCreate(deviceId, group, callback) {
612
- getDeviceSilently(deviceId, group.service, group.subservice, function (error, device) {
604
+ function findOrCreate(deviceId, apikey, group, callback) {
605
+ getDeviceSilently(deviceId, apikey, group.service, group.subservice, function (error, device) {
613
606
  if (!error && device) {
614
- callback(null, device, group);
607
+ if (
608
+ (!('apikey' in device) || device.apikey === undefined) &&
609
+ 'apikey' in group &&
610
+ group.apikey !== undefined
611
+ ) {
612
+ logger.info(context, 'Update provisioned device %j with measure/group apikey %j', device, group.apikey);
613
+ device.apikey = group.apikey; // group apikey is the same of current measure apikey
614
+ updateDevice(device, function (error) {
615
+ callback(error, device, group);
616
+ });
617
+ } else {
618
+ callback(null, device, group);
619
+ }
615
620
  } else if (error.name === 'DEVICE_NOT_FOUND') {
616
621
  const newDevice = {
617
622
  id: deviceId,
@@ -678,7 +683,7 @@ function retrieveDevice(deviceId, apiKey, callback) {
678
683
  async.waterfall(
679
684
  [
680
685
  apply(groupService.get, config.getConfig().defaultResource || '', apiKey),
681
- apply(findOrCreate, deviceId),
686
+ apply(findOrCreate, deviceId, apiKey),
682
687
  apply(
683
688
  mergeDeviceWithConfiguration,
684
689
  ['lazy', 'active', 'staticAttributes', 'commands', 'subscriptions'],
@@ -693,6 +698,7 @@ function retrieveDevice(deviceId, apiKey, callback) {
693
698
  exports.listDevices = intoTrans(context, checkRegistry)(listDevices);
694
699
  exports.listDevicesWithType = intoTrans(context, checkRegistry)(listDevicesWithType);
695
700
  exports.getDevice = intoTrans(context, checkRegistry)(getDevice);
701
+ exports.updateDevice = intoTrans(context, checkRegistry)(updateDevice);
696
702
  exports.getDeviceSilently = intoTrans(context, checkRegistry)(getDeviceSilently);
697
703
  exports.getDevicesByAttribute = intoTrans(context, checkRegistry)(getDevicesByAttribute);
698
704
  exports.getDeviceByName = intoTrans(context, checkRegistry)(getDeviceByName);
@@ -369,7 +369,13 @@ function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) {
369
369
  if (entityInfoUpdated) {
370
370
  async.waterfall(
371
371
  [
372
- apply(config.getRegistry().get, deviceObj.id, deviceObj.service, deviceObj.subservice),
372
+ apply(
373
+ config.getRegistry().get,
374
+ deviceObj.id,
375
+ deviceObj.apikey,
376
+ deviceObj.service,
377
+ deviceObj.subservice
378
+ ),
373
379
  apply(extractDeviceDifference, deviceObj),
374
380
  createInitialEntityNgsiLD,
375
381
  apply(combineWithNewDevice, deviceObj),
@@ -382,7 +388,13 @@ function updateRegisterDeviceNgsiLD(deviceObj, entityInfoUpdated, callback) {
382
388
  } else {
383
389
  async.waterfall(
384
390
  [
385
- apply(config.getRegistry().get, deviceObj.id, deviceObj.service, deviceObj.subservice),
391
+ apply(
392
+ config.getRegistry().get,
393
+ deviceObj.id,
394
+ deviceObj.apikey,
395
+ deviceObj.service,
396
+ deviceObj.subservice
397
+ ),
386
398
  apply(extractDeviceDifference, deviceObj),
387
399
  updateEntityNgsiLD,
388
400
  apply(combineWithNewDevice, deviceObj),
@@ -37,8 +37,6 @@ const deviceHandlerV2 = require('./devices-NGSI-v2');
37
37
  function createInitialEntityNgsiMixed(deviceData, newDevice, callback) {
38
38
  if (config.checkNgsiLD(deviceData)) {
39
39
  deviceHandlerLD.createInitialEntity(deviceData, newDevice, callback);
40
- } else {
41
- deviceHandlerV2.createInitialEntity(deviceData, newDevice, callback);
42
40
  }
43
41
  }
44
42