iotagent-node-lib 2.18.0 → 2.21.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 (141) hide show
  1. package/.github/workflows/ci.yml +1 -2
  2. package/.nyc_output/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +1 -0
  3. package/.nyc_output/processinfo/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +1 -0
  4. package/.nyc_output/processinfo/index.json +1 -0
  5. package/.readthedocs.yml +3 -1
  6. package/CHANGES_NEXT_RELEASE +1 -0
  7. package/README.md +5 -56
  8. package/doc/advanced-topics.md +121 -85
  9. package/doc/api.md +63 -54
  10. package/doc/development.md +8 -9
  11. package/doc/expressionLanguage.md +517 -316
  12. package/doc/installationguide.md +66 -64
  13. package/doc/northboundinteractions.md +40 -33
  14. package/doc/requirements.txt +4 -0
  15. package/{docs → doc}/roadmap.md +21 -6
  16. package/doc/usermanual.md +50 -18
  17. package/docker/Mosquitto/Dockerfile +28 -11
  18. package/docker/Mosquitto/README.md +8 -6
  19. package/docker/Mosquitto/startMosquitto.sh +14 -4
  20. package/lib/fiware-iotagent-lib.js +4 -2
  21. package/lib/jexlTranformsMap.js +11 -1
  22. package/lib/model/Device.js +4 -1
  23. package/lib/model/Group.js +19 -1
  24. package/lib/plugins/expressionParser.js +6 -4
  25. package/lib/plugins/expressionPlugin.js +63 -22
  26. package/lib/plugins/jexlParser.js +3 -1
  27. package/lib/plugins/multiEntity.js +2 -21
  28. package/lib/request-shim.js +111 -0
  29. package/lib/services/common/domain.js +6 -2
  30. package/lib/services/common/genericMiddleware.js +6 -2
  31. package/lib/services/common/iotManagerService.js +1 -1
  32. package/lib/services/common/securityServiceKeystone.js +1 -1
  33. package/lib/services/common/securityServiceOAuth2.js +3 -2
  34. package/lib/services/devices/deviceRegistryMemory.js +13 -2
  35. package/lib/services/devices/deviceRegistryMongoDB.js +16 -7
  36. package/lib/services/devices/deviceService.js +26 -2
  37. package/lib/services/devices/devices-NGSI-LD.js +1 -1
  38. package/lib/services/devices/devices-NGSI-v2.js +2 -6
  39. package/lib/services/devices/registrationUtils.js +0 -2
  40. package/lib/services/ngsi/entities-NGSI-LD.js +97 -11
  41. package/lib/services/ngsi/entities-NGSI-v2.js +95 -8
  42. package/lib/services/ngsi/ngsiService.js +5 -4
  43. package/lib/services/northBound/contextServer-NGSI-LD.js +3 -2
  44. package/lib/services/northBound/contextServer-NGSI-v2.js +32 -27
  45. package/lib/services/northBound/contextServerUtils.js +1 -1
  46. package/lib/services/northBound/deviceProvisioningServer.js +31 -6
  47. package/lib/services/northBound/northboundServer.js +2 -0
  48. package/lib/services/northBound/restUtils.js +1 -1
  49. package/lib/templates/createDevice.json +12 -0
  50. package/lib/templates/updateDevice.json +12 -0
  51. package/package.json +9 -15
  52. package/test/tools/utils.js +2 -0
  53. package/test/unit/expressions/jexlExpression-test.js +5 -5
  54. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +1 -1
  55. package/test/unit/general/deviceService-test.js +2 -5
  56. package/test/unit/general/loglevel-api_test.js +6 -11
  57. package/test/unit/general/startup-test.js +1 -0
  58. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -0
  59. package/test/unit/mongodb/mongodb-group-registry-test.js +1 -1
  60. package/test/unit/mongodb/mongodb-registry-test.js +2 -1
  61. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin12a.json +7 -0
  62. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin13.json +13 -13
  63. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +18 -0
  64. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +18 -0
  65. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin31.json +15 -0
  66. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin32.json +17 -0
  67. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin33.json +18 -0
  68. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin34.json +17 -0
  69. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +36 -0
  70. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin7.json +16 -16
  71. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin8a.json +18 -0
  72. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
  73. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1018 -0
  74. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
  75. package/test/unit/ngsi-ld/general/deviceService-test.js +1 -1
  76. package/test/unit/ngsi-ld/general/https-support-test.js +2 -1
  77. package/test/unit/ngsi-ld/general/iotam-autoregistration-test.js +2 -1
  78. package/test/unit/ngsi-ld/general/startup-test.js +1 -0
  79. package/test/unit/ngsi-ld/lazyAndCommands/active-devices-attribute-update-test.js +3 -1
  80. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +2 -1
  81. package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +2 -6
  82. package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +2 -1
  83. package/test/unit/ngsi-ld/ngsiService/active-devices-test.js +1 -0
  84. package/test/unit/ngsi-ld/ngsiService/autocast-test.js +1 -0
  85. package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +1 -0
  86. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +4 -3
  87. package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +1 -0
  88. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +3 -2
  89. package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +61 -0
  90. package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +2 -1
  91. package/test/unit/ngsi-ld/provisioning/device-registration_test.js +3 -2
  92. package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +1 -0
  93. package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +42 -54
  94. package/test/unit/ngsi-ld/provisioning/provisionDeviceMultientity-test.js +2 -1
  95. package/test/unit/ngsi-ld/provisioning/removeProvisionedDevice-test.js +4 -4
  96. package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +3 -5
  97. package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +12 -18
  98. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +3 -1
  99. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin17.json +1 -1
  100. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +16 -0
  101. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin33.json +22 -0
  102. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +12 -0
  103. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +2 -0
  104. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +1 -0
  105. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
  106. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +25 -0
  107. package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +4 -4
  108. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +561 -0
  109. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +3 -2
  110. package/test/unit/ngsiv2/general/deviceService-test.js +9 -8
  111. package/test/unit/ngsiv2/general/https-support-test.js +2 -1
  112. package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +2 -1
  113. package/test/unit/ngsiv2/general/startup-test.js +1 -0
  114. package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +3 -1
  115. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +2 -1
  116. package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +14 -18
  117. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +3 -1
  118. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +1 -0
  119. package/test/unit/ngsiv2/ngsiService/queryDeviceInformationInCb-test.js +0 -1
  120. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +4 -3
  121. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +3 -2
  122. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +210 -0
  123. package/test/unit/ngsiv2/plugins/translation-inPlugins_test.js +1 -1
  124. package/test/unit/ngsiv2/provisioning/device-group-api-test.js +3 -2
  125. package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +2 -1
  126. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +2 -1
  127. package/test/unit/ngsiv2/provisioning/device-registration_test.js +3 -2
  128. package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +4 -3
  129. package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +42 -53
  130. package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +2 -1
  131. package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +4 -4
  132. package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +3 -4
  133. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +13 -19
  134. package/test/unit/plugins/capture-configuration-inPlugins_test.js +3 -1
  135. package/test/unit/plugins/capture-provision-inPlugins_test.js +2 -1
  136. package/bin/agentConsole.js +0 -257
  137. package/bin/iotAgentTester.js +0 -44
  138. package/lib/command/commandLine.js +0 -918
  139. package/lib/command/migration.js +0 -176
  140. package/test/unit/general/migration-test.js +0 -256
  141. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin20.json +0 -25
@@ -214,7 +214,13 @@ function findConfigurationGroup(deviceObj, callback) {
214
214
  } else {
215
215
  config
216
216
  .getGroupRegistry()
217
- .findTypeSilently(deviceObj.service, deviceObj.subservice, deviceObj.type, deviceObj.apikey, handlerGroupFindByType);
217
+ .findTypeSilently(
218
+ deviceObj.service,
219
+ deviceObj.subservice,
220
+ deviceObj.type,
221
+ deviceObj.apikey,
222
+ handlerGroupFindByType
223
+ );
218
224
  }
219
225
  }
220
226
 
@@ -513,6 +519,18 @@ function getDeviceByName(deviceName, service, subservice, callback) {
513
519
  config.getRegistry().getByName(deviceName, service, subservice, callback);
514
520
  }
515
521
 
522
+ /**
523
+ * Retrieve a device from the registry based on its entity name and type
524
+ *
525
+ * @param {String} deviceName Name of the entity associated to a device.
526
+ * @param {String} deviceType Type of the entity associated to a device.
527
+ * @param {String} service Service the device belongs to.
528
+ * @param {String} subservice Division inside the service.
529
+ */
530
+ function getDeviceByNameAndType(deviceName, deviceType, service, subservice, callback) {
531
+ config.getRegistry().getByNameAndType(deviceName, deviceType, service, subservice, callback);
532
+ }
533
+
516
534
  /**
517
535
  * Retrieve a device from the registry based on the value of a given attribute.
518
536
  *
@@ -581,7 +599,12 @@ function findOrCreate(deviceId, group, callback) {
581
599
  callback(error, device, group);
582
600
  });
583
601
  } else {
584
- logger.info(context, 'Device %j not provisioned due autoprovision is disabled by its conf %j', newDevice, group);
602
+ logger.info(
603
+ context,
604
+ 'Device %j not provisioned due autoprovision is disabled by its conf %j',
605
+ newDevice,
606
+ group
607
+ );
585
608
  callback(new errors.DeviceNotFound(deviceId));
586
609
  }
587
610
  } else {
@@ -632,6 +655,7 @@ exports.getDevice = intoTrans(context, checkRegistry)(getDevice);
632
655
  exports.getDeviceSilently = intoTrans(context, checkRegistry)(getDeviceSilently);
633
656
  exports.getDevicesByAttribute = intoTrans(context, checkRegistry)(getDevicesByAttribute);
634
657
  exports.getDeviceByName = intoTrans(context, checkRegistry)(getDeviceByName);
658
+ exports.getDeviceByNameAndType = intoTrans(context, checkRegistry)(getDeviceByNameAndType);
635
659
  exports.register = intoTrans(context, registerDevice);
636
660
  exports.updateRegister = intoTrans(context, updateRegisterDevice);
637
661
  exports.unregister = intoTrans(context, unregisterDevice);
@@ -23,7 +23,7 @@
23
23
  * Modified by: Jason Fox - FIWARE Foundation
24
24
  */
25
25
 
26
- const request = require('request');
26
+ const request = require('../../request-shim');
27
27
  const async = require('async');
28
28
  const apply = async.apply;
29
29
  const constants = require('../../constants');
@@ -27,7 +27,7 @@
27
27
 
28
28
  /* eslint-disable consistent-return */
29
29
 
30
- const request = require('request');
30
+ const request = require('../../request-shim');
31
31
  const async = require('async');
32
32
  const apply = async.apply;
33
33
  const uuid = require('uuid');
@@ -112,11 +112,7 @@ function createInitialEntityHandlerNgsi2(deviceData, newDevice, callback) {
112
112
  function updateEntityHandlerNgsi2(deviceData, updatedDevice, callback) {
113
113
  return function handleEntityResponse(error, response, body) {
114
114
  if (error) {
115
- logger.error(
116
- context,
117
- 'ORION-001: Connection error updating entity in the Context Broker: %s',
118
- error
119
- );
115
+ logger.error(context, 'ORION-001: Connection error updating entity in the Context Broker: %s', error);
120
116
 
121
117
  alarms.raise(constants.ORION_ALARM, error);
122
118
 
@@ -136,7 +136,6 @@ function sendUnregistrationsNgsi2(deviceData, callback) {
136
136
  const options = {
137
137
  url: cbHost + '/v2/registrations/' + deviceData.registrationId,
138
138
  method: 'DELETE',
139
- json: true,
140
139
  headers: {
141
140
  'fiware-service': deviceData.service,
142
141
  'fiware-servicepath': deviceData.subservice
@@ -177,7 +176,6 @@ function sendUnregistrationsNgsiLD(deviceData, callback) {
177
176
  const options = {
178
177
  url: cbHost + '/ngsi-ld/v1/csourceRegistrations/' + deviceData.registrationId,
179
178
  method: 'DELETE',
180
- json: true,
181
179
  headers: {
182
180
  'fiware-service': deviceData.service,
183
181
  'fiware-servicepath': deviceData.subservice,
@@ -25,7 +25,7 @@
25
25
 
26
26
  /* eslint-disable consistent-return */
27
27
 
28
- const request = require('request');
28
+ const request = require('../../request-shim');
29
29
  const statsService = require('./../stats/statsRegistry');
30
30
  const async = require('async');
31
31
  const apply = async.apply;
@@ -34,6 +34,7 @@ const errors = require('../../errors');
34
34
  const utils = require('../northBound/restUtils');
35
35
  const config = require('../../commonConfig');
36
36
  const constants = require('../../constants');
37
+ const jexlParser = require('../../plugins/jexlParser');
37
38
  const moment = require('moment-timezone');
38
39
  const logger = require('logops');
39
40
  const _ = require('underscore');
@@ -55,8 +56,9 @@ const NGSI_LD_URN = 'urn:ngsi-ld:';
55
56
  * format
56
57
  */
57
58
 
58
- function convertNGSIv2ToLD(attr) {
59
- if (attr.value === null) {
59
+ function convertAttrNGSILD(attr) {
60
+ // eslint eqeqeq - deliberate double equals to include undefined.
61
+ if (attr.value == null || Number.isNaN(attr.value)) {
60
62
  return undefined;
61
63
  }
62
64
  let obj = { type: 'Property', value: attr.value };
@@ -178,7 +180,7 @@ function convertNGSIv2ToLD(attr) {
178
180
  obj.unitCode = attr.metadata[key].value;
179
181
  break;
180
182
  default:
181
- obj[key] = convertNGSIv2ToLD(attr.metadata[key]);
183
+ obj[key] = convertAttrNGSILD(attr.metadata[key]);
182
184
  }
183
185
  });
184
186
  delete obj.TimeInstant;
@@ -214,7 +216,7 @@ function formatAsNGSILD(json) {
214
216
  // element for NSGI-LD.
215
217
  break;
216
218
  default:
217
- obj[key] = convertNGSIv2ToLD(json[key]);
219
+ obj[key] = convertAttrNGSILD(json[key]);
218
220
  }
219
221
  });
220
222
 
@@ -329,7 +331,6 @@ function sendQueryValueNgsiLD(entityName, attributes, typeInformation, token, ca
329
331
 
330
332
  const options = NGSIUtils.createRequestObject(url, typeInformation, token);
331
333
  options.method = 'GET';
332
- options.json = true;
333
334
 
334
335
  if (!typeInformation || !typeInformation.type) {
335
336
  callback(new errors.TypeNotFound(null, entityName));
@@ -402,6 +403,92 @@ function addLinkedEntities(typeInformation, json) {
402
403
  });
403
404
  }
404
405
 
406
+ /**
407
+ * Remove id, type and any hidden attrs after processing
408
+ *
409
+ * @param {Object} enities Unprocessed entities
410
+ * @param {Object} typeInformation Configuration information for the device.
411
+ */
412
+ function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
413
+ let explicitAttrsList;
414
+ if (typeInformation.explicitAttrs) {
415
+ explicitAttrsList = [];
416
+ explicitAttrsList.push('type');
417
+ explicitAttrsList.push('id');
418
+ if (typeof typeInformation.explicitAttrs === 'boolean') {
419
+ if (typeInformation.timestamp) {
420
+ explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
421
+ }
422
+ if (typeInformation.active) {
423
+ typeInformation.active.forEach((attr) => {
424
+ explicitAttrsList.push(attr.name);
425
+ });
426
+ }
427
+ if (typeInformation.staticAttributes) {
428
+ typeInformation.staticAttributes.forEach((attr) => {
429
+ explicitAttrsList.push(attr.name);
430
+ });
431
+ }
432
+ } else if (typeof typeInformation.explicitAttrs === 'string') {
433
+ entities.forEach((entity) => {
434
+ const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
435
+ const ctx = jexlParser.extractContext(attsArray);
436
+ const res = jexlParser.parse(typeInformation.explicitAttrs, ctx);
437
+ explicitAttrsList = explicitAttrsList.concat(res);
438
+ });
439
+ }
440
+ }
441
+ if (explicitAttrsList && explicitAttrsList.length >= 0) {
442
+ entities.forEach((entity) => {
443
+ const hidden = _.difference(_.keys(entity), explicitAttrsList);
444
+ hidden.forEach((attr) => {
445
+ delete entity[attr];
446
+ });
447
+ });
448
+ }
449
+ return entities;
450
+ }
451
+
452
+ /**
453
+ * Remove id, type and any hidden attrs after processing
454
+ *
455
+ * @param {Object} result Unprocessed entity
456
+ * @param {Object} typeInformation Configuration information for the device.
457
+ */
458
+ function removeHiddenAttrs(result, typeInformation) {
459
+ delete result.id;
460
+ delete result.type;
461
+ let explicitAttrsList;
462
+ if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
463
+ explicitAttrsList = [];
464
+ if (typeInformation.timestamp) {
465
+ explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
466
+ }
467
+ if (typeInformation.active) {
468
+ typeInformation.active.forEach((attr) => {
469
+ explicitAttrsList.push(attr.name);
470
+ });
471
+ }
472
+ if (typeInformation.staticAttributes) {
473
+ typeInformation.staticAttributes.forEach((attr) => {
474
+ explicitAttrsList.push(attr.name);
475
+ });
476
+ }
477
+ } else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'string') {
478
+ const attsArray = utils.extractAttributesArrayFromNgsi2Entity(result);
479
+ const ctx = jexlParser.extractContext(attsArray);
480
+ const res = jexlParser.parse(typeInformation.explicitAttrs, ctx);
481
+ explicitAttrsList = res;
482
+ }
483
+ if (explicitAttrsList && explicitAttrsList.length >= 0) {
484
+ const hidden = _.difference(_.keys(result), explicitAttrsList);
485
+ hidden.forEach((attr) => {
486
+ delete result[attr];
487
+ });
488
+ }
489
+ return result;
490
+ }
491
+
405
492
  /**
406
493
  * Makes an update in the Device's entity in the context broker, with the values given in the 'attributes' array.
407
494
  * This array should comply to the NGSI-LD's attribute format.
@@ -413,7 +500,6 @@ function addLinkedEntities(typeInformation, json) {
413
500
  */
414
501
  function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, callback) {
415
502
  let payload = {};
416
-
417
503
  const url = '/ngsi-ld/v1/entityOperations/upsert/?options=update';
418
504
 
419
505
  const options = NGSIUtils.createRequestObject(url, typeInformation, token);
@@ -474,11 +560,10 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c
474
560
  }
475
561
  }
476
562
 
477
- options.json = result;
563
+ options.json = removeHiddenAttrsFromMultiEntity(result, typeInformation);
478
564
  } else {
479
- delete result.id;
480
- delete result.type;
481
- options.json = result;
565
+ // Remove id, type and any hidden attrs after processing
566
+ options.json = removeHiddenAttrs(result, typeInformation);
482
567
  logger.debug(context, 'typeInformation: %j', typeInformation);
483
568
  if (
484
569
  'timestamp' in typeInformation && typeInformation.timestamp !== undefined
@@ -567,6 +652,7 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c
567
652
  );
568
653
  }
569
654
 
655
+ exports.convertAttrNGSILD = convertAttrNGSILD;
570
656
  exports.formatAsNGSILD = formatAsNGSILD;
571
657
  exports.sendUpdateValue = sendUpdateValueNgsiLD;
572
658
  exports.sendQueryValue = sendQueryValueNgsiLD;
@@ -27,21 +27,24 @@
27
27
 
28
28
  /* eslint-disable consistent-return */
29
29
 
30
- const request = require('request');
30
+ const request = require('../../request-shim');
31
31
  const statsService = require('./../stats/statsRegistry');
32
32
  const async = require('async');
33
33
  const apply = async.apply;
34
34
  const alarms = require('../common/alarmManagement');
35
35
  const errors = require('../../errors');
36
36
  const utils = require('../northBound/restUtils');
37
+ const pluginUtils = require('../../plugins/pluginUtils');
37
38
  const config = require('../../commonConfig');
38
39
  const constants = require('../../constants');
40
+ const jexlParser = require('../../plugins/jexlParser');
39
41
  const moment = require('moment-timezone');
40
42
  const NGSIUtils = require('./ngsiUtils');
41
43
  const logger = require('logops');
42
44
  const context = {
43
45
  op: 'IoTAgentNGSI.Entities-v2'
44
46
  };
47
+ const _ = require('underscore');
45
48
 
46
49
  /**
47
50
  * Amends an NGSIv2 Geoattribute from String to GeoJSON format
@@ -290,7 +293,6 @@ function sendQueryValueNgsi2(entityName, attributes, typeInformation, token, cal
290
293
 
291
294
  const options = NGSIUtils.createRequestObject(url, typeInformation, token);
292
295
  options.method = 'GET';
293
- options.json = true;
294
296
 
295
297
  if (!typeInformation || !typeInformation.type) {
296
298
  callback(new errors.TypeNotFound(null, entityName));
@@ -312,6 +314,94 @@ function sendQueryValueNgsi2(entityName, attributes, typeInformation, token, cal
312
314
  );
313
315
  }
314
316
 
317
+ /**
318
+ * Remove id, type and any hidden attrs after processing
319
+ *
320
+ * @param {Object} enities Unprocessed entities
321
+ * @param {Object} typeInformation Configuration information for the device.
322
+ */
323
+ function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
324
+ let explicitAttrsList;
325
+ if (typeInformation.explicitAttrs) {
326
+ explicitAttrsList = [];
327
+ explicitAttrsList.push('type');
328
+ explicitAttrsList.push('id');
329
+ if (typeof typeInformation.explicitAttrs === 'boolean') {
330
+ if (typeInformation.timestamp) {
331
+ explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
332
+ }
333
+
334
+ if (typeInformation.active) {
335
+ typeInformation.active.forEach((attr) => {
336
+ explicitAttrsList.push(attr.name);
337
+ });
338
+ }
339
+ if (typeInformation.staticAttributes) {
340
+ typeInformation.staticAttributes.forEach((attr) => {
341
+ explicitAttrsList.push(attr.name);
342
+ });
343
+ }
344
+ } else if (typeof typeInformation.explicitAttrs === 'string') {
345
+ entities.forEach((entity) => {
346
+ const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(entity);
347
+ const ctx = jexlParser.extractContext(attsArray);
348
+ const res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctx, typeInformation);
349
+ explicitAttrsList = explicitAttrsList.concat(res);
350
+ });
351
+ }
352
+ }
353
+ if (explicitAttrsList && explicitAttrsList.length >= 0) {
354
+ entities.forEach((entity) => {
355
+ const hidden = _.difference(_.keys(entity), explicitAttrsList);
356
+ logger.debug(context, 'removeHiddenAttrsFromMultiEntity %s from entity %s', hidden, entity);
357
+ hidden.forEach((attr) => {
358
+ delete entity[attr];
359
+ });
360
+ });
361
+ }
362
+ return entities;
363
+ }
364
+ /**
365
+ * Remove id, type and any hidden attrs after processing
366
+ *
367
+ * @param {Object} result An Unprocessed entity
368
+ * @param {Object} typeInformation Configuration information for the device.
369
+ */
370
+ function removeHiddenAttrs(result, typeInformation) {
371
+ delete result.id;
372
+ delete result.type;
373
+ let explicitAttrsList;
374
+ if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
375
+ explicitAttrsList = [];
376
+ if (typeInformation.timestamp) {
377
+ explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
378
+ }
379
+ if (typeInformation.active) {
380
+ typeInformation.active.forEach((attr) => {
381
+ explicitAttrsList.push(attr.name);
382
+ });
383
+ }
384
+ if (typeInformation.staticAttributes) {
385
+ typeInformation.staticAttributes.forEach((attr) => {
386
+ explicitAttrsList.push(attr.name);
387
+ });
388
+ }
389
+ } else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'string') {
390
+ const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(result);
391
+ const ctx = jexlParser.extractContext(attsArray);
392
+ const res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctx, typeInformation);
393
+ explicitAttrsList = res;
394
+ }
395
+ if (explicitAttrsList && explicitAttrsList.length >= 0) {
396
+ const hidden = _.difference(_.keys(result), explicitAttrsList);
397
+ logger.debug(context, 'removeHiddenAttrs %s', hidden);
398
+ hidden.forEach((attr) => {
399
+ delete result[attr];
400
+ });
401
+ }
402
+ return result;
403
+ }
404
+
315
405
  /**
316
406
  * Makes an update in the Device's entity in the context broker, with the values given in the 'attributes' array. This
317
407
  * array should comply to the NGSIv2's attribute format.
@@ -326,7 +416,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
326
416
 
327
417
  let url = '/v2/entities/' + entityName + '/attrs';
328
418
 
329
- if (typeInformation.type) {
419
+ if (typeInformation && typeInformation.type) {
330
420
  url += '?type=' + typeInformation.type;
331
421
  }
332
422
 
@@ -359,7 +449,6 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
359
449
  //overwritte if id or type missnamed atributes reach this point
360
450
  payload.id = entityName;
361
451
  payload.type = typeInformation.type;
362
-
363
452
  payload = NGSIUtils.castJsonNativeAttributes(payload);
364
453
  async.waterfall(
365
454
  [
@@ -392,7 +481,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
392
481
  }
393
482
  options.json = {
394
483
  actionType: 'append',
395
- entities: result
484
+ entities: removeHiddenAttrsFromMultiEntity(result, typeInformation)
396
485
  };
397
486
  if (config.getConfig().appendMode === true) {
398
487
  options.json.actionType = 'append';
@@ -400,9 +489,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
400
489
  options.json.actionType = 'update';
401
490
  }
402
491
  } else {
403
- delete result.id;
404
- delete result.type;
405
- options.json = result;
492
+ options.json = removeHiddenAttrs(result, typeInformation);
406
493
  logger.debug(context, 'typeInformation: %j', typeInformation);
407
494
  if (
408
495
  'timestamp' in typeInformation && typeInformation.timestamp !== undefined
@@ -132,9 +132,10 @@ function executeWithDeviceInformation(operationFunction) {
132
132
  attributes,
133
133
  deviceInformation
134
134
  );
135
- config.getGroupRegistry().getType(type, function (error, deviceGroup) {
135
+ const currentType = type ? type : deviceInformation.type;
136
+ config.getGroupRegistry().getType(currentType, function (error, deviceGroup) {
136
137
  let typeInformation;
137
- const configDeviceInfo = config.getConfig().types[type];
138
+ const configDeviceInfo = config.getConfig().types[currentType];
138
139
  if (error) {
139
140
  logger.debug(context, 'error %j in get group device', error);
140
141
  }
@@ -176,7 +177,7 @@ function executeWithDeviceInformation(operationFunction) {
176
177
 
177
178
  /**
178
179
  * Update the result of a command in the Context Broker. The result of the command has two components: the result
179
- * of the command itself will be represented with the sufix '_result' in the entity while the status is updated in the
180
+ * of the command itself will be represented with the sufix '_info' in the entity while the status is updated in the
180
181
  * attribute with the '_status' sufix.
181
182
  *
182
183
  * @param {String} entityName Name of the entity holding the command.
@@ -233,7 +234,7 @@ function setCommandResult(
233
234
  }
234
235
 
235
236
  if (commandInfo.length === 1) {
236
- exports.update(entityName, resource, apikey, attributes, typeInformation, callback);
237
+ exports.update(entityName, typeInformation.type, apikey, attributes, typeInformation, callback);
237
238
  } else {
238
239
  callback(new errors.CommandNotFound(commandName));
239
240
  }
@@ -40,6 +40,7 @@ const context = {
40
40
  const updateContextTemplateNgsiLD = require('../../templates/updateContextNgsiLD.json');
41
41
  const notificationTemplateNgsiLD = require('../../templates/notificationTemplateNgsiLD.json');
42
42
  const contextServerUtils = require('./contextServerUtils');
43
+ const ngsiLD = require('../ngsi/entities-NGSI-LD');
43
44
 
44
45
  const updatePaths = ['/ngsi-ld/v1/entities/:entity/attrs/:attr'];
45
46
  const queryPaths = ['/ngsi-ld/v1/entities/:entity'];
@@ -250,10 +251,10 @@ function defaultQueryHandlerNgsiLD(id, type, service, subservice, attributes, ca
250
251
  attributeType = lazyAttribute.type;
251
252
  }
252
253
 
253
- contextElement[attributes[i]] = {
254
+ contextElement[attributes[i]] = ngsiLD.convertAttrNGSILD({
254
255
  type: attributeType,
255
256
  value: ''
256
- };
257
+ });
257
258
  }
258
259
 
259
260
  callback(null, contextElement);
@@ -159,29 +159,32 @@ function generateUpdateActionsNgsi2(req, contextElement, callback) {
159
159
  callback(null, updateActions);
160
160
  }
161
161
 
162
- deviceService.getDeviceByName(entityId, req.headers['fiware-service'], req.headers['fiware-servicepath'], function (
163
- error,
164
- deviceObj
165
- ) {
166
- if (error) {
167
- callback(error);
168
- } else {
169
- async.waterfall(
170
- [
171
- apply(deviceService.findConfigurationGroup, deviceObj),
172
- apply(
173
- deviceService.mergeDeviceWithConfiguration,
174
- ['lazy', 'internalAttributes', 'active', 'staticAttributes', 'commands', 'subscriptions'],
175
- [null, null, [], [], [], [], []],
176
- deviceObj
177
- ),
178
- splitUpdates,
179
- createActionsArray
180
- ],
181
- callback
182
- );
162
+ deviceService.getDeviceByNameAndType(
163
+ entityId,
164
+ entityType,
165
+ req.headers['fiware-service'],
166
+ req.headers['fiware-servicepath'],
167
+ function (error, deviceObj) {
168
+ if (error) {
169
+ callback(error);
170
+ } else {
171
+ async.waterfall(
172
+ [
173
+ apply(deviceService.findConfigurationGroup, deviceObj),
174
+ apply(
175
+ deviceService.mergeDeviceWithConfiguration,
176
+ ['lazy', 'internalAttributes', 'active', 'staticAttributes', 'commands', 'subscriptions'],
177
+ [null, null, [], [], [], [], []],
178
+ deviceObj
179
+ ),
180
+ splitUpdates,
181
+ createActionsArray
182
+ ],
183
+ callback
184
+ );
185
+ }
183
186
  }
184
- });
187
+ );
185
188
  }
186
189
 
187
190
  /** Express middleware to manage incoming update requests using NGSIv2.
@@ -238,8 +241,7 @@ function defaultQueryHandlerNgsi2(id, type, service, subservice, attributes, cal
238
241
  type,
239
242
  id
240
243
  };
241
-
242
- deviceService.getDeviceByName(id, service, subservice, function (error, ngsiDevice) {
244
+ deviceService.getDeviceByNameAndType(id, type, service, subservice, function (error, ngsiDevice) {
243
245
  if (error) {
244
246
  callback(error);
245
247
  } else {
@@ -285,8 +287,9 @@ function handleNotificationNgsi2(req, res, next) {
285
287
  }
286
288
  }
287
289
  }
288
- deviceService.getDeviceByName(
290
+ deviceService.getDeviceByNameAndType(
289
291
  dataElement.id,
292
+ dataElement.type,
290
293
  req.headers['fiware-service'],
291
294
  req.headers['fiware-servicepath'],
292
295
  function (error, device) {
@@ -448,6 +451,7 @@ function handleQueryNgsi2(req, res, next) {
448
451
  }
449
452
 
450
453
  deviceService.findConfigurationGroup(device, function (error, group) {
454
+ logger.debug(context, 'finishQueryForDevice %j', group);
451
455
  const executeCompleteAttributes = apply(completeAttributes, attributes, group);
452
456
  const executeQueryHandler = apply(
453
457
  actualHandler,
@@ -474,8 +478,9 @@ function handleQueryNgsi2(req, res, next) {
474
478
 
475
479
  if (contextEntity.id) {
476
480
  getFunction = apply(
477
- deviceService.getDeviceByName,
481
+ deviceService.getDeviceByNameAndType,
478
482
  contextEntity.id,
483
+ contextEntity.type,
479
484
  req.headers['fiware-service'],
480
485
  req.headers['fiware-servicepath']
481
486
  );
@@ -504,7 +509,7 @@ function handleQueryNgsi2(req, res, next) {
504
509
  } else {
505
510
  deviceList = [innerDevice];
506
511
  }
507
-
512
+ logger.debug(context, 'handleFindDevice from %j', deviceList);
508
513
  async.map(
509
514
  deviceList,
510
515
  async.apply(finishQueryForDevice, attributes, contextEntity, actualHandler),
@@ -91,7 +91,7 @@ function executeUpdateSideEffects(device, id, type, service, subservice, attribu
91
91
  ];
92
92
 
93
93
  sideEffects.push(
94
- apply(ngsi.update, device.name, device.resource, device.apikey, newAttributes, device)
94
+ apply(ngsi.update, device.name, device.type, device.apikey, newAttributes, device)
95
95
  );
96
96
  }
97
97
  }