iotagent-node-lib 4.8.0 → 4.10.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 (124) hide show
  1. package/.github/workflows/ci.yml +10 -9
  2. package/.nyc_output/e4b1fe20-197e-4221-9f8d-6db65ff234b2.json +1 -0
  3. package/.nyc_output/processinfo/e4b1fe20-197e-4221-9f8d-6db65ff234b2.json +1 -0
  4. package/.nyc_output/processinfo/index.json +1 -0
  5. package/Changelog +791 -0
  6. package/config.js +2 -1
  7. package/doc/admin.md +20 -4
  8. package/doc/api.md +34 -4
  9. package/doc/devel/northboundinteractions.md +200 -112
  10. package/lib/commonConfig.js +16 -3
  11. package/lib/model/Device.js +3 -1
  12. package/lib/model/Group.js +2 -1
  13. package/lib/services/common/domain.js +4 -3
  14. package/lib/services/common/iotManagerService.js +2 -1
  15. package/lib/services/devices/deviceRegistryMongoDB.js +5 -1
  16. package/lib/services/devices/deviceService.js +17 -1
  17. package/lib/services/devices/devices-NGSI-LD.js +4 -4
  18. package/lib/services/devices/devices-NGSI-mixed.js +16 -0
  19. package/lib/services/devices/devices-NGSI-v2.js +123 -10
  20. package/lib/services/devices/registrationUtils.js +97 -30
  21. package/lib/services/groups/groupRegistryMongoDB.js +2 -1
  22. package/lib/services/ngsi/entities-NGSI-LD.js +69 -13
  23. package/lib/services/ngsi/ngsiService.js +3 -1
  24. package/lib/services/ngsi/subscription-NGSI-LD.js +2 -2
  25. package/lib/services/ngsi/subscription-NGSI-mixed.js +3 -3
  26. package/lib/services/ngsi/subscription-NGSI-v2.js +20 -6
  27. package/lib/services/ngsi/subscriptionService.js +2 -2
  28. package/lib/services/northBound/contextServer-NGSI-LD.js +5 -3
  29. package/lib/services/northBound/deviceProvisioningServer.js +6 -3
  30. package/lib/services/northBound/northboundServer.js +1 -1
  31. package/lib/services/northBound/restUtils.js +6 -2
  32. package/lib/templates/updateDevice.json +12 -0
  33. package/lib/templates/updateDeviceLax.json +4 -0
  34. package/package.json +2 -2
  35. package/test/functional/config-test.js +1 -1
  36. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +2 -2
  37. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -1
  38. package/test/unit/ngsi-ld/examples/contextRequests/createDatetimeProvisionedDevice.json +1 -4
  39. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDevice.json +3 -12
  40. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceMultientity.json +3 -12
  41. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic.json +6 -24
  42. package/test/unit/ngsi-ld/examples/contextRequests/createProvisionedDeviceWithGroupAndStatic2.json +1 -4
  43. package/test/unit/ngsi-ld/examples/contextRequests/updateContext1.json +1 -4
  44. package/test/unit/ngsi-ld/examples/contextRequests/updateContext3WithStatic.json +1 -4
  45. package/test/unit/ngsi-ld/examples/contextRequests/updateContext4.json +1 -4
  46. package/test/unit/ngsi-ld/examples/contextRequests/updateContext5.json +1 -4
  47. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin6.json +1 -4
  48. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin7.json +1 -4
  49. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin8.json +4 -2
  50. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin9.json +1 -4
  51. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandError.json +3 -9
  52. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandExpired.json +13 -19
  53. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandFinish.json +13 -19
  54. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCommandStatus1.json +4 -9
  55. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp1.json +1 -4
  56. package/test/unit/ngsi-ld/examples/contextRequests/updateContextCompressTimestamp2.json +1 -4
  57. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +1 -4
  58. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin2.json +2 -8
  59. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +1 -4
  60. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4.json +2 -8
  61. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +3 -12
  62. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin6.json +3 -5
  63. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin7.json +1 -4
  64. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin8a.json +1 -4
  65. package/test/unit/ngsi-ld/examples/contextRequests/updateContextJsonProperty.json +13 -0
  66. package/test/unit/ngsi-ld/examples/contextRequests/updateContextListProperty.json +14 -0
  67. package/test/unit/ngsi-ld/examples/contextRequests/updateContextListRelationship.json +14 -0
  68. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin1.json +2 -8
  69. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin2.json +2 -8
  70. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin3.json +3 -8
  71. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin4.json +1 -4
  72. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin5.json +2 -8
  73. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin8.json +2 -8
  74. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +2 -8
  75. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +1 -4
  76. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -4
  77. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +2 -8
  78. package/test/unit/ngsi-ld/examples/contextRequests/updateContextRelationship.json +11 -0
  79. package/test/unit/ngsi-ld/examples/contextRequests/updateContextValueType1.json +51 -0
  80. package/test/unit/ngsi-ld/examples/contextRequests/updateContextValueType2.json +65 -0
  81. package/test/unit/ngsi-ld/examples/contextRequests/updateContextVocabProperty.json +11 -0
  82. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionCommands1.json +2 -8
  83. package/test/unit/ngsi-ld/examples/contextRequests/updateProvisionDeviceStatic.json +1 -4
  84. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
  85. package/test/unit/ngsi-ld/general/https-support-test.js +1 -1
  86. package/test/unit/ngsi-ld/general/startup-test.js +3 -0
  87. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +1 -1
  88. package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +71 -1
  89. package/test/unit/ngsi-ld/ngsiService/attributeTypes-test.js +293 -0
  90. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +6 -6
  91. package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +0 -10
  92. package/test/unit/ngsi-ld/ngsiService/value-types-test.js +221 -0
  93. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands.json +24 -0
  94. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands2.json +24 -0
  95. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands3.json +24 -0
  96. package/test/unit/ngsiv2/examples/contextAvailabilityRequests/subscribeIoTAgentCommands4.json +24 -0
  97. package/test/unit/ngsiv2/examples/contextRequests/updateEntity.json +5 -0
  98. package/test/unit/ngsiv2/examples/contextRequests/updateEntity2.json +5 -0
  99. package/test/unit/ngsiv2/examples/contextRequests/updateEntity2b.json +7 -0
  100. package/test/unit/ngsiv2/examples/contextRequests/updateEntity3.json +5 -0
  101. package/test/unit/ngsiv2/examples/subscriptionRequests/simpleSubscriptionRequest.json +4 -2
  102. package/test/unit/ngsiv2/examples/subscriptionRequests/simpleSubscriptionRequest2.json +4 -2
  103. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
  104. package/test/unit/ngsiv2/general/https-support-test.js +1 -1
  105. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +245 -2
  106. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast1.json +0 -11
  107. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json +0 -14
  108. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast2.json +0 -11
  109. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast3.json +0 -11
  110. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast4.json +0 -11
  111. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast5.json +0 -7
  112. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast6.json +0 -17
  113. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast7.json +0 -19
  114. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json +0 -14
  115. package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json +0 -14
  116. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +0 -109
  117. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast1.json +0 -8
  118. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast2.json +0 -8
  119. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast3.json +0 -8
  120. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast4.json +0 -8
  121. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast5.json +0 -8
  122. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast6.json +0 -8
  123. package/test/unit/ngsiv2/examples/contextRequests/updateContextAutocast7.json +0 -8
  124. /package/test/unit/ngsi-ld/examples/contextRequests/{updateContextLanguageProperties1.json → updateContextLanguageProperty.json} +0 -0
@@ -157,9 +157,11 @@ function processEnvironmentVariables() {
157
157
  'IOTA_FALLBACK_PATH',
158
158
  'IOTA_LD_SUPPORT_NULL',
159
159
  'IOTA_LD_SUPPORT_DATASET_ID',
160
+ 'IOTA_LD_SUPPORT_DATA_TYPE',
160
161
  'IOTA_EXPRESS_LIMIT',
161
162
  'IOTA_USE_CB_FLOW_CONTROL',
162
- 'IOTA_STORE_LAST_MEASURE'
163
+ 'IOTA_STORE_LAST_MEASURE',
164
+ 'IOTA_CMD_MODE'
163
165
  ];
164
166
  const iotamVariables = [
165
167
  'IOTA_IOTAM_URL',
@@ -264,7 +266,12 @@ function processEnvironmentVariables() {
264
266
  config.server.port = process.env.IOTA_NORTH_PORT;
265
267
  }
266
268
 
267
- config.server.ldSupport = config.server.ldSupport || { null: true, datasetId: true, merge: false };
269
+ config.server.ldSupport = config.server.ldSupport || {
270
+ null: true,
271
+ datasetId: true,
272
+ merge: false,
273
+ dataType: 'none'
274
+ };
268
275
 
269
276
  if (process.env.IOTA_LD_SUPPORT_NULL) {
270
277
  config.server.ldSupport.null = process.env.IOTA_LD_SUPPORT_NULL === 'true';
@@ -275,6 +282,9 @@ function processEnvironmentVariables() {
275
282
  if (process.env.IOTA_LD_SUPPORT_MERGE) {
276
283
  config.server.ldSupport.datasetId = process.env.IOTA_LD_SUPPORT_MERGE === 'true';
277
284
  }
285
+ if (process.env.IOTA_LD_SUPPORT_DATA_TYPE) {
286
+ config.server.ldSupport.dataType = process.env.IOTA_LD_SUPPORT_DATA_TYPE;
287
+ }
278
288
 
279
289
  if (process.env.IOTA_PROVIDER_URL) {
280
290
  config.providerUrl = process.env.IOTA_PROVIDER_URL;
@@ -491,6 +501,9 @@ function processEnvironmentVariables() {
491
501
  } else {
492
502
  config.storeLastMeasure = config.storeLastMeasure === true;
493
503
  }
504
+ if (process.env.IOTA_CMD_MODE) {
505
+ config.cmdMode = process.env.IOTA_CMD_MODE;
506
+ }
494
507
  }
495
508
 
496
509
  function setConfig(newConfig) {
@@ -511,7 +524,7 @@ function getConfig() {
511
524
  function getConfigForTypeInformation() {
512
525
  // Just return relevant configuration flags
513
526
  // avoid to include server, authentication, mongodb, orion and iotamanger info
514
- let conf = {
527
+ const conf = {
515
528
  timestamp: config.timestamp,
516
529
  defaultResource: config.defaultResource,
517
530
  explicitAttrs: config.explicitAttrs,
@@ -57,7 +57,9 @@ const Device = new Schema({
57
57
  useCBflowControl: Boolean,
58
58
  storeLastMeasure: Boolean,
59
59
  lastMeasure: Object,
60
- oldCtxt: Object
60
+ oldCtxt: Object,
61
+ cmdMode: String,
62
+ subscriptionId: String
61
63
  });
62
64
 
63
65
  function load() {
@@ -67,7 +67,8 @@ const Group = new Schema({
67
67
  entityNameExp: String,
68
68
  payloadType: String,
69
69
  useCBflowControl: Boolean,
70
- storeLastMeasure: Boolean
70
+ storeLastMeasure: Boolean,
71
+ cmdMode: String
71
72
  });
72
73
 
73
74
  function load() {
@@ -68,7 +68,7 @@ function requestDomain(req, res, next) {
68
68
  reqDomain.path = req.path;
69
69
  reqDomain.op = req.url;
70
70
  reqDomain.start = Date.now();
71
-
71
+ reqDomain.from = req.ip || req.connection.remoteAddress;
72
72
  reqDomain.add(req);
73
73
  reqDomain.add(res);
74
74
 
@@ -145,14 +145,15 @@ function ensureSouthboundTransaction(context, callback) {
145
145
  if (context.op) {
146
146
  reqDomain.op = context.op;
147
147
  }
148
-
149
148
  if (context.srv) {
150
149
  reqDomain.service = context.srv;
151
150
  }
152
-
153
151
  if (context.subsrv) {
154
152
  reqDomain.subservice = context.subsrv;
155
153
  }
154
+ if (context.from) {
155
+ reqDomain.from = context.from;
156
+ }
156
157
  }
157
158
 
158
159
  return reqDomain.run(callback);
@@ -65,7 +65,8 @@ function register(callback) {
65
65
  endpoint: service.endpoint,
66
66
  transport: service.transport,
67
67
  useCBflowControl: service.useCBflowControl,
68
- storeLastMeasure: service.storeLastMeasure
68
+ storeLastMeasure: service.storeLastMeasure,
69
+ cmdMode: service.cmdMode
69
70
  };
70
71
  }
71
72
 
@@ -59,7 +59,9 @@ const attributeList = [
59
59
  'subscriptions',
60
60
  'payloadType',
61
61
  'useCBflowControl',
62
- 'storeLastMeasure'
62
+ 'storeLastMeasure',
63
+ 'cmdMode',
64
+ 'subscriptionId'
63
65
  ];
64
66
 
65
67
  /**
@@ -324,6 +326,8 @@ function update(previousDevice, device, callback) {
324
326
  data.useCBflowControl = device.useCBflowControl;
325
327
  data.storeLastMeasure = device.storeLastMeasure;
326
328
  data.lastMeasure = device.lastMeasure;
329
+ data.cmdMode = device.cmdMode;
330
+ data.subscriptionId = device.subscriptionId;
327
331
 
328
332
  /* eslint-disable-next-line new-cap */
329
333
  const deviceObj = new Device.model(data);
@@ -66,6 +66,17 @@ function init() {
66
66
  }
67
67
  }
68
68
 
69
+ /**
70
+ * Creates the initial entity representing the device in the Context Broker. This is important mainly to allow the
71
+ * rest of the updateContext operations to be performed using an UPDATE action instead of an APPEND one.
72
+ *
73
+ * @param {Object} deviceData Object containing all the deviceData needed to send the registration.
74
+ * @param {Object} newDevice Device object that will be stored in the database.
75
+ */
76
+ function createInitialEntity(deviceData, newDevice, callback) {
77
+ deviceHandler.createInitialEntity(deviceData, newDevice, callback);
78
+ }
79
+
69
80
  /**
70
81
  * If the object_id or the name of the attribute is missing, complete it with the other piece of data.
71
82
  *
@@ -187,6 +198,9 @@ function mergeDeviceWithConfiguration(fields, defaults, deviceData, configuratio
187
198
  if (configuration && configuration.storeLastMeasure !== undefined && deviceData.storeLastMeasure === undefined) {
188
199
  deviceData.storeLastMeasure = configuration.storeLastMeasure;
189
200
  }
201
+ if (configuration && configuration.cmdMode !== undefined && deviceData.cmdMode === undefined) {
202
+ deviceData.cmdMode = configuration.cmdMode;
203
+ }
190
204
  logger.debug(context, 'deviceData after merge with conf: %j', deviceData);
191
205
  callback(null, deviceData);
192
206
  }
@@ -361,13 +375,15 @@ function registerDevice(deviceObj, callback) {
361
375
  async.waterfall(
362
376
  [
363
377
  apply(registrationUtils.sendRegistrations, false, deviceData),
364
- apply(registrationUtils.processContextRegistration, deviceData)
378
+ apply(registrationUtils.processContextRegistration, deviceData),
379
+ apply(createInitialEntity, deviceData)
365
380
  ],
366
381
  function (error, results) {
367
382
  if (error) {
368
383
  callback(error);
369
384
  } else {
370
385
  deviceObj.registrationId = results.registrationId;
386
+ deviceObj.subscriptionId = results.subscriptionId;
371
387
  deviceObj.name = deviceData.name;
372
388
  deviceObj.service = deviceData.service;
373
389
  deviceObj.subservice = deviceData.subservice;
@@ -33,7 +33,6 @@ const logger = require('logops');
33
33
  const config = require('../../commonConfig');
34
34
  const ngsiLD = require('../ngsi/entities-NGSI-LD');
35
35
  const utils = require('../northBound/restUtils');
36
- const moment = require('moment');
37
36
  const _ = require('underscore');
38
37
  const registrationUtils = require('./registrationUtils');
39
38
  const NGSIv2 = require('./devices-NGSI-v2');
@@ -107,7 +106,7 @@ function updateEntityHandlerNgsiLD(deviceData, updatedDevice, callback) {
107
106
  * @param {Object} deviceData Object containing all the deviceData needed to send the registration.
108
107
  * @param {Object} newDevice Device object that will be stored in the database.
109
108
  */
110
- function createInitialEntityNgsiLDFake(deviceData, newDevice, callback) {
109
+ function createInitialEntityNgsiLD(deviceData, newDevice, callback) {
111
110
  callback(null, newDevice);
112
111
  }
113
112
 
@@ -153,7 +152,7 @@ function updateEntityNgsiLD(deviceData, updatedDevice, callback) {
153
152
  ) {
154
153
  options.json[constants.TIMESTAMP_ATTRIBUTE] = {
155
154
  type: constants.TIMESTAMP_TYPE_NGSI2,
156
- value: moment()
155
+ value: new Date().toISOString()
157
156
  };
158
157
  }
159
158
 
@@ -285,7 +284,7 @@ function updateRegisterDeviceNgsiLD(deviceObj, previousDevice, entityInfoUpdated
285
284
  deviceObj.subservice
286
285
  ),
287
286
  apply(extractDeviceDifference, deviceObj),
288
- createInitialEntityNgsiLDFake,
287
+ createInitialEntityNgsiLD,
289
288
  apply(combineWithNewDevice, deviceObj),
290
289
  apply(registrationUtils.sendRegistrations, false),
291
290
  apply(registrationUtils.processContextRegistration, deviceObj),
@@ -315,4 +314,5 @@ function updateRegisterDeviceNgsiLD(deviceObj, previousDevice, entityInfoUpdated
315
314
  }
316
315
  }
317
316
 
317
+ exports.createInitialEntity = createInitialEntityNgsiLD;
318
318
  exports.updateRegisterDevice = updateRegisterDeviceNgsiLD;
@@ -27,6 +27,21 @@ const config = require('../../commonConfig');
27
27
  const deviceHandlerLD = require('./devices-NGSI-LD');
28
28
  const deviceHandlerV2 = require('./devices-NGSI-v2');
29
29
 
30
+ /**
31
+ * Creates the initial entity representing the device in the Context Broker using both NGSI-LD and NGSI-v2
32
+ * This is important mainly to allow the rest of the updateContext operations to be performed.
33
+ *
34
+ * @param {Object} deviceData Object containing all the deviceData needed to send the registration.
35
+ * @param {Object} newDevice Device object that will be stored in the database.
36
+ */
37
+ function createInitialEntityNgsiMixed(deviceData, newDevice, callback) {
38
+ if (config.checkNgsiLD(deviceData)) {
39
+ deviceHandlerLD.createInitialEntity(deviceData, newDevice, callback);
40
+ } else {
41
+ deviceHandlerV2.createInitialEntity(deviceData, newDevice, callback);
42
+ }
43
+ }
44
+
30
45
  /**
31
46
  * Updates the register of an existing device identified by the Id and Type in the Context Broker, and the internal
32
47
  * registry. It uses both NGSI-LD and NGSI-v2
@@ -46,4 +61,5 @@ function updateRegisterDeviceNgsiMixed(deviceObj, previousDevice, entityInfoUpda
46
61
  }
47
62
  }
48
63
 
64
+ exports.createInitialEntity = createInitialEntityNgsiMixed;
49
65
  exports.updateRegisterDevice = updateRegisterDeviceNgsiMixed;
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright 2020 Telefonica Investigación y Desarrollo, S.A.U
2
+ * Copyright 2020 Telefonica Investigación y Desarrollo, S.A.U
3
3
  *
4
4
  * This file is part of fiware-iotagent-lib
5
5
  *
@@ -41,7 +41,6 @@ const registrationUtils = require('./registrationUtils');
41
41
  const _ = require('underscore');
42
42
  const utils = require('../northBound/restUtils');
43
43
  const NGSIv2 = require('../ngsi/entities-NGSI-v2');
44
- const moment = require('moment');
45
44
  const context = {
46
45
  op: 'IoTAgentNGSI.Devices-v2'
47
46
  };
@@ -165,12 +164,102 @@ function formatCommandsNgsi2(originalVector) {
165
164
  return attributeList;
166
165
  }
167
166
 
167
+ function formatCommandsBySubsNgsi2(originalVector) {
168
+ const attributeList = {};
169
+
170
+ if (originalVector && originalVector.length) {
171
+ for (let i = 0; i < originalVector.length; i++) {
172
+ attributeList[originalVector[i].name] = {
173
+ type: 'command',
174
+ value: null
175
+ };
176
+ }
177
+ }
178
+
179
+ return attributeList;
180
+ }
181
+
182
+ function createInitialEntityHandlerNgsi2(deviceData, newDevice, callback) {
183
+ return function handleInitialEntityResponse(error, response, body) {
184
+ if (error) {
185
+ logger.error(
186
+ context,
187
+ 'ORION-001: Connection error creating inital entity in the Context Broker: %s',
188
+ error
189
+ );
190
+
191
+ alarms.raise(constants.ORION_ALARM, error);
192
+
193
+ callback(error);
194
+ } else if (response && response.statusCode === 204) {
195
+ alarms.release(constants.ORION_ALARM);
196
+ logger.debug(context, 'Initial entity created successfully.');
197
+ callback(null, newDevice);
198
+ } else {
199
+ logger.error(
200
+ context,
201
+ 'Protocol error connecting to the Context Broker [%d]: %s',
202
+ response.statusCode,
203
+ body
204
+ );
205
+
206
+ const errorObj = new errors.EntityGenericError(newDevice.id, newDevice.type, body, response.statusCode);
207
+
208
+ callback(errorObj);
209
+ }
210
+ };
211
+ }
212
+
168
213
  /*
169
214
  * This methods makes a bypass in updateRegisterDeviceNgsi2 to allow not change
170
215
  * extractDeviceDifference and combineWithNewDevice methods
171
216
  */
172
- function createInitialEntityNgsi2Fake(deviceData, newDevice, callback) {
173
- callback(null, newDevice);
217
+ function createInitialEntityNgsi2(newDevice, deviceObj, callback) {
218
+ logger.debug(context, 'createInitialEntityNgsiv2 called with newDevice: %j', newDevice);
219
+
220
+ const cmdModeConfig = config.getConfig().cmdMode;
221
+
222
+ const isLegacy =
223
+ (!newDevice.cmdMode && (!cmdModeConfig || cmdModeConfig === 'legacy')) || newDevice.cmdMode === 'legacy';
224
+ if (isLegacy) {
225
+ logger.debug(context, 'Old cmdMode with newDevice: %j', deviceObj);
226
+ return callback(null, deviceObj);
227
+ }
228
+
229
+ if (!Array.isArray(newDevice.commands) || newDevice.commands.length === 0) {
230
+ logger.debug(context, 'No initial entity due to no commands by subs in newDevice: %j', newDevice);
231
+ return callback(null, deviceObj);
232
+ }
233
+
234
+ const entityPayload = {
235
+ id: String(newDevice.name),
236
+ type: newDevice.type
237
+ };
238
+ jsonConcat(entityPayload, formatCommandsBySubsNgsi2(newDevice.commands));
239
+
240
+ let url = config.getConfig().contextBroker.url + '/v2/entities?options=upsert';
241
+ if (newDevice.cbHost) {
242
+ url = newDevice.cbHost.includes('://')
243
+ ? newDevice.cbHost + '/v2/entities?options=upsert'
244
+ : 'http://' + newDevice.cbHost + '/v2/entities?options=upsert';
245
+ }
246
+
247
+ const headers = {
248
+ 'fiware-correlator': (domain.active && domain.active.corr) || uuid.v4()
249
+ };
250
+ if (newDevice.service) headers['fiware-service'] = newDevice.service;
251
+ if (newDevice.subservice) headers['fiware-servicepath'] = newDevice.subservice;
252
+
253
+ const options = { url, method: 'POST', json: entityPayload, headers };
254
+
255
+ logger.debug(context, 'Creating initial entity due to commands by subs with newDevice: %j', newDevice);
256
+ logger.debug(context, 'Creating initial entity in the Context Broker:\n %s', JSON.stringify(options, null, 4));
257
+
258
+ return utils.executeWithSecurity(
259
+ options,
260
+ newDevice,
261
+ createInitialEntityHandlerNgsi2(newDevice, deviceObj, callback)
262
+ );
174
263
  }
175
264
 
176
265
  /**
@@ -180,6 +269,7 @@ function createInitialEntityNgsi2Fake(deviceData, newDevice, callback) {
180
269
  * @param {Object} updatedDevice Device object that will be stored in the database.
181
270
  */
182
271
  function updateEntityNgsi2(deviceData, updatedDevice, callback) {
272
+ logger.debug(context, 'updateEntityNgsi2 called with deviceData: %j updatedDevice: %j', deviceData, updatedDevice);
183
273
  const options = {
184
274
  url: config.getConfig().contextBroker.url + '/v2/entities/' + String(deviceData.name) + '/attrs',
185
275
  method: 'POST',
@@ -203,7 +293,14 @@ function updateEntityNgsi2(deviceData, updatedDevice, callback) {
203
293
 
204
294
  jsonConcat(options.json, formatAttributesNgsi2(deviceData.active, false));
205
295
  jsonConcat(options.json, formatAttributesNgsi2(deviceData.staticAttributes, true));
206
- jsonConcat(options.json, formatCommandsNgsi2(deviceData.commands));
296
+ if (
297
+ (!updatedDevice.cmdMode && (!config.getConfig().cmdMode || config.getConfig().cmdMode === 'legacy')) ||
298
+ (updatedDevice && updatedDevice.cmdMode === 'legacy')
299
+ ) {
300
+ jsonConcat(options.json, formatCommandsNgsi2(deviceData.commands));
301
+ } else {
302
+ jsonConcat(options.json, formatCommandsBySubsNgsi2(deviceData.commands));
303
+ }
207
304
 
208
305
  for (const att in options.json) {
209
306
  try {
@@ -222,7 +319,7 @@ function updateEntityNgsi2(deviceData, updatedDevice, callback) {
222
319
  ) {
223
320
  options.json[constants.TIMESTAMP_ATTRIBUTE] = {
224
321
  type: constants.TIMESTAMP_TYPE_NGSI2,
225
- value: moment()
322
+ value: new Date().toISOString()
226
323
  };
227
324
  }
228
325
 
@@ -253,7 +350,13 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
253
350
  return;
254
351
  }
255
352
 
256
- logger.debug(context, 'Update provisioned v2 device in Device Service %j %j', deviceObj, entityInfoUpdated);
353
+ logger.debug(
354
+ context,
355
+ 'Update provisioned v2 device %j with Device %j entityInfoUpdated %j',
356
+ previousDevice,
357
+ deviceObj,
358
+ entityInfoUpdated
359
+ );
257
360
 
258
361
  function combineWithNewDevice(newDevice, oldDevice, callback) {
259
362
  logger.debug(context, 'combineWithNewDevice %j %j', newDevice, oldDevice);
@@ -291,6 +394,9 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
291
394
  if ('storeLastMeasure' in newDevice && newDevice.storeLastMeasure !== undefined) {
292
395
  oldDevice.storeLastMeasure = newDevice.storeLastMeasure;
293
396
  }
397
+ if ('cmdMode' in newDevice && newDevice.cmdMode !== undefined) {
398
+ oldDevice.cmdMode = newDevice.cmdMode;
399
+ }
294
400
  callback(null, oldDevice);
295
401
  } else {
296
402
  callback(new errors.DeviceNotFound(newDevice.id, newDevice));
@@ -338,7 +444,7 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
338
444
  if (entityInfoUpdated) {
339
445
  jsonConcat(deviceData.active, oldDevice.active);
340
446
  jsonConcat(deviceData.lazy, oldDevice.lazy);
341
- jsonConcat(deviceData.commands, oldDevice.commands);
447
+ deviceData.commands = deviceData.commands.concat(oldDevice.commands);
342
448
  jsonConcat(deviceData.staticAttributes, oldDevice.staticAttributes);
343
449
  if (oldDevice.name !== newDevice.name) {
344
450
  deviceData.name = newDevice.name;
@@ -347,7 +453,13 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
347
453
  deviceData.type = newDevice.type;
348
454
  }
349
455
  }
350
-
456
+ logger.debug(
457
+ context,
458
+ 'extractDeviceDifference newDevice %j oldDevice %j difference %j',
459
+ newDevice,
460
+ oldDevice,
461
+ deviceData
462
+ );
351
463
  callback(null, deviceData, oldDevice);
352
464
  }
353
465
 
@@ -362,7 +474,7 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
362
474
  deviceObj.subservice
363
475
  ),
364
476
  apply(extractDeviceDifference, deviceObj),
365
- createInitialEntityNgsi2Fake,
477
+ createInitialEntityNgsi2,
366
478
  apply(combineWithNewDevice, deviceObj),
367
479
  apply(registrationUtils.sendRegistrations, false),
368
480
  apply(registrationUtils.processContextRegistration, deviceObj),
@@ -392,6 +504,7 @@ function updateRegisterDeviceNgsi2(deviceObj, previousDevice, entityInfoUpdated,
392
504
  }
393
505
  }
394
506
 
507
+ exports.createInitialEntity = createInitialEntityNgsi2;
395
508
  exports.updateRegisterDevice = updateRegisterDeviceNgsi2;
396
509
  exports.formatCommands = formatCommandsNgsi2;
397
510
  exports.formatAttributes = formatAttributesNgsi2;
@@ -36,6 +36,7 @@ const context = {
36
36
  };
37
37
  const async = require('async');
38
38
  const utils = require('../northBound/restUtils');
39
+ const subscriptionService = require('../ngsi/subscriptionService');
39
40
 
40
41
  const NGSI_LD_URN = 'urn:ngsi-ld:';
41
42
 
@@ -203,6 +204,23 @@ function sendUnregistrationsNgsiLD(deviceData, callback) {
203
204
  return callback(null, deviceData);
204
205
  }
205
206
 
207
+ function formatAttributes(originalVector) {
208
+ const attributeList = [];
209
+ if (originalVector && originalVector.length) {
210
+ for (let i = 0; i < originalVector.length; i++) {
211
+ attributeList.push(originalVector[i].name);
212
+ }
213
+ }
214
+ return attributeList;
215
+ }
216
+
217
+ function mergeWithSameName(old, current) {
218
+ if (old.indexOf(current) < 0) {
219
+ old.push(current);
220
+ }
221
+ return old;
222
+ }
223
+
206
224
  /**
207
225
  * Sends a Context Provider registration or unregistration request to the Context Broker using NGSIv2.
208
226
  *
@@ -210,26 +228,6 @@ function sendUnregistrationsNgsiLD(deviceData, callback) {
210
228
  * @param {Object} deviceData Object containing all the deviceData needed to send the registration.
211
229
  */
212
230
  function sendRegistrationsNgsi2(unregister, deviceData, callback) {
213
- function formatAttributes(originalVector) {
214
- const attributeList = [];
215
-
216
- if (originalVector && originalVector.length) {
217
- for (let i = 0; i < originalVector.length; i++) {
218
- attributeList.push(originalVector[i].name);
219
- }
220
- }
221
-
222
- return attributeList;
223
- }
224
-
225
- function mergeWithSameName(old, current) {
226
- if (old.indexOf(current) < 0) {
227
- old.push(current);
228
- }
229
-
230
- return old;
231
- }
232
-
233
231
  // FIXME: When https://github.com/telefonicaid/fiware-orion/issues/3007 is merged into master branch,
234
232
  // this function should use the new API. This is just a temporary solution which implies deleting the
235
233
  // registration and creating a new one.
@@ -299,6 +297,61 @@ function sendRegistrationsNgsi2(unregister, deviceData, callback) {
299
297
  utils.executeWithSecurity(options, deviceData, createRegistrationHandlerNgsi2(unregister, deviceData, callback));
300
298
  }
301
299
 
300
+ function sendUnsubscriptionsNgsi2(deviceData, callback) {
301
+ if (deviceData.subscriptionId) {
302
+ logger.debug(
303
+ context,
304
+ 'Sending v2 device unsubscriptions to Context Broker at [%s]',
305
+ config.getConfig().contextBroker.url
306
+ );
307
+ logger.debug(context, 'Using the following subscriptionId %j', deviceData.subscriptionId);
308
+ subscriptionService.unsubscribe(deviceData, deviceData.subscriptionId, callback);
309
+ } else {
310
+ logger.debug(context, 'No subscription found for unregister');
311
+ return callback(null, deviceData);
312
+ }
313
+ }
314
+
315
+ function sendSubscriptionsNgsi2(unregister, deviceData, callback) {
316
+ function updateSubscriptionNgsi2(deviceData, callback) {
317
+ const functions = [];
318
+
319
+ function removeSubscriptionId(deviceData, unregistrationResult, callback) {
320
+ delete deviceData.subscriptionId;
321
+ return callback(null, deviceData);
322
+ }
323
+
324
+ functions.push(async.apply(sendSubscriptionsNgsi2, true, deviceData));
325
+ functions.push(async.apply(removeSubscriptionId, deviceData));
326
+ functions.push(async.apply(sendSubscriptionsNgsi2, false));
327
+ async.waterfall(functions, callback);
328
+ }
329
+
330
+ if (unregister) {
331
+ return sendUnsubscriptionsNgsi2(deviceData, callback);
332
+ }
333
+ if (deviceData.subscriptionId) {
334
+ return updateSubscriptionNgsi2(deviceData, callback);
335
+ }
336
+ const attrs = [].concat(formatAttributes(deviceData.commands)).reduce(mergeWithSameName, []);
337
+
338
+ if (attrs.length === 0) {
339
+ logger.debug(context, 'Subscription is not needed. Device without commands');
340
+ return callback(null, deviceData);
341
+ }
342
+ const trigger = attrs; // one subscription for all commands
343
+ const content = attrs;
344
+ const attrsFormat = 'simplifiedNormalized';
345
+
346
+ logger.debug(
347
+ context,
348
+ 'Sending v2 device subscriptions to Context Broker at [%s]',
349
+ config.getConfig().contextBroker.url
350
+ );
351
+ logger.debug(context, 'Using the following trigger %j and content %j', trigger, content);
352
+ subscriptionService.subscribe(deviceData, trigger, content, attrsFormat, callback);
353
+ }
354
+
302
355
  /**
303
356
  * Sends a Context Provider registration or unregistration request to the Context Broker using NGSI-LD.
304
357
  *
@@ -323,14 +376,14 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
323
376
  properties.push(element.name);
324
377
  });
325
378
 
326
- if (lazy.length > 0){
327
- operations.push('retrieveOps');
379
+ if (lazy.length > 0) {
380
+ operations.push('retrieveOps');
328
381
  }
329
- if (commands.length > 0){
330
- operations.push('updateOps');
382
+ if (commands.length > 0) {
383
+ operations.push('updateOps');
331
384
  }
332
- if (supportMerge){
333
- operations.push('mergeEntity');
385
+ if (supportMerge) {
386
+ operations.push('mergeEntity');
334
387
  }
335
388
 
336
389
  if (properties.length === 0) {
@@ -368,8 +421,8 @@ function sendRegistrationsNgsiLD(unregister, deviceData, callback) {
368
421
  endpoint: config.getConfig().providerUrl,
369
422
  contextSourceInfo: [
370
423
  {
371
- 'key': 'jsonldContext',
372
- 'value': config.getConfig().contextBroker.jsonLdContext
424
+ key: 'jsonldContext',
425
+ value: config.getConfig().contextBroker.jsonLdContext
373
426
  }
374
427
  ],
375
428
  '@context': config.getConfig().contextBroker.jsonLdContext
@@ -405,7 +458,14 @@ function sendRegistrations(unregister, deviceData, callback) {
405
458
  sendRegistrationsNgsiLD(unregister, deviceData, callback);
406
459
  break;
407
460
  default:
408
- sendRegistrationsNgsi2(unregister, deviceData, callback);
461
+ if (
462
+ (!deviceData.cmdMode && (!config.getConfig().cmdMode || config.getConfig().cmdMode === 'legacy')) ||
463
+ (deviceData && deviceData.cmdMode === 'legacy')
464
+ ) {
465
+ sendRegistrationsNgsi2(unregister, deviceData, callback);
466
+ } else {
467
+ sendSubscriptionsNgsi2(unregister, deviceData, callback);
468
+ }
409
469
  break;
410
470
  }
411
471
  }
@@ -421,7 +481,14 @@ function processContextRegistration(deviceData, body, callback) {
421
481
  const newDevice = _.clone(deviceData);
422
482
 
423
483
  if (body) {
424
- newDevice.registrationId = body.registrationId;
484
+ if (
485
+ (!deviceData.cmdMode && (!config.getConfig().cmdMode || config.getConfig().cmdMode === 'legacy')) ||
486
+ (deviceData && deviceData.cmdMode === 'legacy')
487
+ ) {
488
+ newDevice.registrationId = body.registrationId;
489
+ } else {
490
+ newDevice.subscriptionId = body.subscriptionId;
491
+ }
425
492
  }
426
493
 
427
494
  callback(null, newDevice);
@@ -63,7 +63,8 @@ const attributeList = [
63
63
  'entityNameExp',
64
64
  'payloadType',
65
65
  'useCBflowControl',
66
- 'storeLastMeasure'
66
+ 'storeLastMeasure',
67
+ 'cmdMode'
67
68
  ];
68
69
 
69
70
  function createGroup(group, callback) {