iotagent-node-lib 2.18.0 → 2.19.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 (119) hide show
  1. package/.github/workflows/ci.yml +1 -2
  2. package/doc/advanced-topics.md +122 -53
  3. package/doc/api.md +52 -52
  4. package/doc/development.md +8 -9
  5. package/doc/expressionLanguage.md +514 -316
  6. package/doc/installationguide.md +66 -64
  7. package/doc/usermanual.md +48 -16
  8. package/docker/Mosquitto/Dockerfile +1 -0
  9. package/docker/Mosquitto/README.md +1 -0
  10. package/docker/Mosquitto/startMosquitto.sh +6 -4
  11. package/lib/command/commandLine.js +1 -1
  12. package/lib/fiware-iotagent-lib.js +3 -0
  13. package/lib/jexlTranformsMap.js +9 -1
  14. package/lib/model/Device.js +4 -1
  15. package/lib/model/Group.js +19 -1
  16. package/lib/plugins/expressionParser.js +6 -4
  17. package/lib/plugins/expressionPlugin.js +8 -1
  18. package/lib/plugins/jexlParser.js +3 -1
  19. package/lib/request-shim.js +111 -0
  20. package/lib/services/common/genericMiddleware.js +6 -2
  21. package/lib/services/common/iotManagerService.js +1 -1
  22. package/lib/services/common/securityServiceKeystone.js +1 -1
  23. package/lib/services/common/securityServiceOAuth2.js +3 -2
  24. package/lib/services/devices/deviceRegistryMongoDB.js +1 -0
  25. package/lib/services/devices/devices-NGSI-LD.js +1 -1
  26. package/lib/services/devices/devices-NGSI-v2.js +2 -6
  27. package/lib/services/devices/registrationUtils.js +0 -2
  28. package/lib/services/ngsi/entities-NGSI-LD.js +95 -11
  29. package/lib/services/ngsi/entities-NGSI-v2.js +93 -8
  30. package/lib/services/ngsi/ngsiService.js +3 -2
  31. package/lib/services/northBound/contextServer-NGSI-LD.js +3 -2
  32. package/lib/services/northBound/deviceProvisioningServer.js +29 -6
  33. package/lib/services/northBound/northboundServer.js +2 -0
  34. package/lib/services/northBound/restUtils.js +1 -1
  35. package/package.json +4 -4
  36. package/test/tools/utils.js +2 -0
  37. package/test/unit/expressions/jexlExpression-test.js +5 -5
  38. package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +1 -1
  39. package/test/unit/general/deviceService-test.js +2 -5
  40. package/test/unit/general/loglevel-api_test.js +6 -11
  41. package/test/unit/general/migration-test.js +1 -0
  42. package/test/unit/general/startup-test.js +1 -0
  43. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -0
  44. package/test/unit/mongodb/mongodb-group-registry-test.js +1 -1
  45. package/test/unit/mongodb/mongodb-registry-test.js +2 -1
  46. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin12a.json +7 -0
  47. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin13.json +13 -13
  48. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin1a.json +18 -0
  49. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin29.json +18 -0
  50. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin31.json +15 -0
  51. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin32.json +17 -0
  52. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin33.json +18 -0
  53. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin34.json +17 -0
  54. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +36 -0
  55. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin7.json +16 -16
  56. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin8a.json +18 -0
  57. package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
  58. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1018 -0
  59. package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -2
  60. package/test/unit/ngsi-ld/general/deviceService-test.js +1 -1
  61. package/test/unit/ngsi-ld/general/https-support-test.js +2 -1
  62. package/test/unit/ngsi-ld/general/iotam-autoregistration-test.js +2 -1
  63. package/test/unit/ngsi-ld/general/startup-test.js +1 -0
  64. package/test/unit/ngsi-ld/lazyAndCommands/active-devices-attribute-update-test.js +3 -1
  65. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +2 -1
  66. package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +2 -6
  67. package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +2 -1
  68. package/test/unit/ngsi-ld/ngsiService/active-devices-test.js +1 -0
  69. package/test/unit/ngsi-ld/ngsiService/autocast-test.js +1 -0
  70. package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +1 -0
  71. package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +4 -3
  72. package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +1 -0
  73. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +3 -2
  74. package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +61 -0
  75. package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +2 -1
  76. package/test/unit/ngsi-ld/provisioning/device-registration_test.js +3 -2
  77. package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +1 -0
  78. package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +42 -54
  79. package/test/unit/ngsi-ld/provisioning/provisionDeviceMultientity-test.js +2 -1
  80. package/test/unit/ngsi-ld/provisioning/removeProvisionedDevice-test.js +4 -4
  81. package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +3 -5
  82. package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +12 -18
  83. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +3 -1
  84. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin17.json +1 -1
  85. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +16 -0
  86. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin33.json +22 -0
  87. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json +12 -0
  88. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin15.json +25 -0
  89. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin16.json +25 -0
  90. package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +4 -4
  91. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +500 -0
  92. package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +3 -2
  93. package/test/unit/ngsiv2/general/deviceService-test.js +9 -8
  94. package/test/unit/ngsiv2/general/https-support-test.js +2 -1
  95. package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +2 -1
  96. package/test/unit/ngsiv2/general/startup-test.js +1 -0
  97. package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +3 -1
  98. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +2 -1
  99. package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +14 -18
  100. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +3 -1
  101. package/test/unit/ngsiv2/ngsiService/active-devices-test.js +1 -0
  102. package/test/unit/ngsiv2/ngsiService/queryDeviceInformationInCb-test.js +0 -1
  103. package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +4 -3
  104. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +3 -2
  105. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +210 -0
  106. package/test/unit/ngsiv2/plugins/translation-inPlugins_test.js +1 -1
  107. package/test/unit/ngsiv2/provisioning/device-group-api-test.js +3 -2
  108. package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +2 -1
  109. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +2 -1
  110. package/test/unit/ngsiv2/provisioning/device-registration_test.js +3 -2
  111. package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +4 -3
  112. package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +42 -53
  113. package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +2 -1
  114. package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +4 -4
  115. package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +3 -4
  116. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +13 -19
  117. package/test/unit/plugins/capture-configuration-inPlugins_test.js +3 -1
  118. package/test/unit/plugins/capture-provision-inPlugins_test.js +2 -1
  119. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin20.json +0 -25
@@ -23,7 +23,7 @@
23
23
  * Modified by: Federico M. Facca - Martel Innovate
24
24
  */
25
25
 
26
- const request = require('request');
26
+ const request = require('../../request-shim');
27
27
  const async = require('async');
28
28
  const errors = require('../../errors');
29
29
  const constants = require('../../constants');
@@ -21,7 +21,7 @@
21
21
  * please contact with::daniel.moranjimenez@telefonica.com
22
22
  */
23
23
 
24
- const request = require('request');
24
+ const request = require('../../request-shim');
25
25
  const errors = require('../../errors');
26
26
  const config = require('../../commonConfig');
27
27
  const intoTrans = require('../common/domain').intoTrans;
@@ -26,7 +26,7 @@
26
26
 
27
27
  /* eslint-disable consistent-return */
28
28
 
29
- const request = require('request');
29
+ const request = require('../../request-shim');
30
30
  const queryString = require('query-string');
31
31
  const errors = require('../../errors');
32
32
  const config = require('../../commonConfig');
@@ -64,7 +64,8 @@ function auth(trust, callback) {
64
64
  'Content-Length': formData.length,
65
65
  'Content-Type': 'application/x-www-form-urlencoded'
66
66
  },
67
- body: formData
67
+ body: formData,
68
+ responseType: 'text'
68
69
  };
69
70
 
70
71
  logger.debug(context, 'Authentication on the OAuth2 provider [%s]', url);
@@ -289,6 +289,7 @@ function update(device, callback) {
289
289
  data.internalAttributes = device.internalAttributes;
290
290
  data.commands = device.commands;
291
291
  data.endpoint = device.endpoint;
292
+ data.polling = device.polling;
292
293
  data.name = device.name;
293
294
  data.type = device.type;
294
295
  data.apikey = device.apikey;
@@ -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,90 @@ 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
+ var explicitAttrsList = [];
414
+ if (typeInformation.explicitAttrs) {
415
+ explicitAttrsList.push('type');
416
+ explicitAttrsList.push('id');
417
+ if (typeof typeInformation.explicitAttrs === 'boolean') {
418
+ if (typeInformation.timestamp) {
419
+ explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
420
+ }
421
+ if (typeInformation.active) {
422
+ typeInformation.active.forEach((attr) => {
423
+ explicitAttrsList.push(attr.name);
424
+ });
425
+ }
426
+ if (typeInformation.staticAttributes) {
427
+ typeInformation.staticAttributes.forEach((attr) => {
428
+ explicitAttrsList.push(attr.name);
429
+ });
430
+ }
431
+ } else if (typeof typeInformation.explicitAttrs === 'string') {
432
+ entities.forEach((entity) => {
433
+ const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
434
+ const ctx = jexlParser.extractContext(attsArray);
435
+ const res = jexlParser.parse(typeInformation.explicitAttrs, ctx);
436
+ explicitAttrsList = explicitAttrsList.concat(res);
437
+ });
438
+ }
439
+ }
440
+ if (explicitAttrsList.length > 0) {
441
+ entities.forEach((entity) => {
442
+ const hidden = _.difference(_.keys(entity), explicitAttrsList);
443
+ hidden.forEach((attr) => {
444
+ delete entity[attr];
445
+ });
446
+ });
447
+ }
448
+ return entities;
449
+ }
450
+
451
+ /**
452
+ * Remove id, type and any hidden attrs after processing
453
+ *
454
+ * @param {Object} result Unprocessed entity
455
+ * @param {Object} typeInformation Configuration information for the device.
456
+ */
457
+ function removeHiddenAttrs(result, typeInformation) {
458
+ delete result.id;
459
+ delete result.type;
460
+ var explicitAttrsList = [];
461
+ if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
462
+ if (typeInformation.timestamp) {
463
+ explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
464
+ }
465
+ if (typeInformation.active) {
466
+ typeInformation.active.forEach((attr) => {
467
+ explicitAttrsList.push(attr.name);
468
+ });
469
+ }
470
+ if (typeInformation.staticAttributes) {
471
+ typeInformation.staticAttributes.forEach((attr) => {
472
+ explicitAttrsList.push(attr.name);
473
+ });
474
+ }
475
+ } else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'string') {
476
+ const attsArray = utils.extractAttributesArrayFromNgsi2Entity(result);
477
+ const ctx = jexlParser.extractContext(attsArray);
478
+ const res = jexlParser.parse(typeInformation.explicitAttrs, ctx);
479
+ explicitAttrsList = res;
480
+ }
481
+ if (explicitAttrsList.length > 0) {
482
+ const hidden = _.difference(_.keys(result), explicitAttrsList);
483
+ hidden.forEach((attr) => {
484
+ delete result[attr];
485
+ });
486
+ }
487
+ return result;
488
+ }
489
+
405
490
  /**
406
491
  * Makes an update in the Device's entity in the context broker, with the values given in the 'attributes' array.
407
492
  * This array should comply to the NGSI-LD's attribute format.
@@ -413,7 +498,6 @@ function addLinkedEntities(typeInformation, json) {
413
498
  */
414
499
  function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, callback) {
415
500
  let payload = {};
416
-
417
501
  const url = '/ngsi-ld/v1/entityOperations/upsert/?options=update';
418
502
 
419
503
  const options = NGSIUtils.createRequestObject(url, typeInformation, token);
@@ -474,11 +558,10 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c
474
558
  }
475
559
  }
476
560
 
477
- options.json = result;
561
+ options.json = removeHiddenAttrsFromMultiEntity(result, typeInformation);
478
562
  } else {
479
- delete result.id;
480
- delete result.type;
481
- options.json = result;
563
+ // Remove id, type and any hidden attrs after processing
564
+ options.json = removeHiddenAttrs(result, typeInformation);
482
565
  logger.debug(context, 'typeInformation: %j', typeInformation);
483
566
  if (
484
567
  'timestamp' in typeInformation && typeInformation.timestamp !== undefined
@@ -567,6 +650,7 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c
567
650
  );
568
651
  }
569
652
 
653
+ exports.convertAttrNGSILD = convertAttrNGSILD;
570
654
  exports.formatAsNGSILD = formatAsNGSILD;
571
655
  exports.sendUpdateValue = sendUpdateValueNgsiLD;
572
656
  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,92 @@ 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
+ var explicitAttrsList = [];
325
+ if (typeInformation.explicitAttrs) {
326
+ explicitAttrsList.push('type');
327
+ explicitAttrsList.push('id');
328
+ if (typeof typeInformation.explicitAttrs === 'boolean') {
329
+ if (typeInformation.timestamp) {
330
+ explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
331
+ }
332
+
333
+ if (typeInformation.active) {
334
+ typeInformation.active.forEach((attr) => {
335
+ explicitAttrsList.push(attr.name);
336
+ });
337
+ }
338
+ if (typeInformation.staticAttributes) {
339
+ typeInformation.staticAttributes.forEach((attr) => {
340
+ explicitAttrsList.push(attr.name);
341
+ });
342
+ }
343
+ } else if (typeof typeInformation.explicitAttrs === 'string') {
344
+ entities.forEach((entity) => {
345
+ const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(entity);
346
+ const ctx = jexlParser.extractContext(attsArray);
347
+ const res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctx, typeInformation);
348
+ explicitAttrsList = explicitAttrsList.concat(res);
349
+ });
350
+ }
351
+ }
352
+ if (explicitAttrsList.length > 0) {
353
+ logger.debug(context, 'removeHiddenAttrsFromMultiEntity %s', explicitAttrsList);
354
+ entities.forEach((entity) => {
355
+ const hidden = _.difference(_.keys(entity), explicitAttrsList);
356
+ hidden.forEach((attr) => {
357
+ delete entity[attr];
358
+ });
359
+ });
360
+ }
361
+ return entities;
362
+ }
363
+ /**
364
+ * Remove id, type and any hidden attrs after processing
365
+ *
366
+ * @param {Object} result An Unprocessed entity
367
+ * @param {Object} typeInformation Configuration information for the device.
368
+ */
369
+ function removeHiddenAttrs(result, typeInformation) {
370
+ delete result.id;
371
+ delete result.type;
372
+ var explicitAttrsList = [];
373
+ if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
374
+ if (typeInformation.timestamp) {
375
+ explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
376
+ }
377
+ if (typeInformation.active) {
378
+ typeInformation.active.forEach((attr) => {
379
+ explicitAttrsList.push(attr.name);
380
+ });
381
+ }
382
+ if (typeInformation.staticAttributes) {
383
+ typeInformation.staticAttributes.forEach((attr) => {
384
+ explicitAttrsList.push(attr.name);
385
+ });
386
+ }
387
+ } else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'string') {
388
+ const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(result);
389
+ const ctx = jexlParser.extractContext(attsArray);
390
+ const res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctx, typeInformation);
391
+ explicitAttrsList = res;
392
+ }
393
+ if (explicitAttrsList.length > 0) {
394
+ logger.debug(context, 'removeHiddenAttrs %s', explicitAttrsList);
395
+ const hidden = _.difference(_.keys(result), explicitAttrsList);
396
+ hidden.forEach((attr) => {
397
+ delete result[attr];
398
+ });
399
+ }
400
+ return result;
401
+ }
402
+
315
403
  /**
316
404
  * Makes an update in the Device's entity in the context broker, with the values given in the 'attributes' array. This
317
405
  * array should comply to the NGSIv2's attribute format.
@@ -326,7 +414,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
326
414
 
327
415
  let url = '/v2/entities/' + entityName + '/attrs';
328
416
 
329
- if (typeInformation.type) {
417
+ if (typeInformation && typeInformation.type) {
330
418
  url += '?type=' + typeInformation.type;
331
419
  }
332
420
 
@@ -359,7 +447,6 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
359
447
  //overwritte if id or type missnamed atributes reach this point
360
448
  payload.id = entityName;
361
449
  payload.type = typeInformation.type;
362
-
363
450
  payload = NGSIUtils.castJsonNativeAttributes(payload);
364
451
  async.waterfall(
365
452
  [
@@ -392,7 +479,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
392
479
  }
393
480
  options.json = {
394
481
  actionType: 'append',
395
- entities: result
482
+ entities: removeHiddenAttrsFromMultiEntity(result, typeInformation)
396
483
  };
397
484
  if (config.getConfig().appendMode === true) {
398
485
  options.json.actionType = 'append';
@@ -400,9 +487,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
400
487
  options.json.actionType = 'update';
401
488
  }
402
489
  } else {
403
- delete result.id;
404
- delete result.type;
405
- options.json = result;
490
+ options.json = removeHiddenAttrs(result, typeInformation);
406
491
  logger.debug(context, 'typeInformation: %j', typeInformation);
407
492
  if (
408
493
  '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
  }
@@ -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);
@@ -34,6 +34,7 @@ const context = {
34
34
  };
35
35
  const apply = async.apply;
36
36
  let provisioningHandler;
37
+ let updatingHandler;
37
38
  let removeDeviceHandler;
38
39
  let updateDeviceTemplate;
39
40
  let createDeviceTemplate;
@@ -53,6 +54,7 @@ const provisioningAPITranslation = {
53
54
  protocol: 'protocol',
54
55
  transport: 'transport',
55
56
  endpoint: 'endpoint',
57
+ polling: 'polling',
56
58
  attributes: 'active',
57
59
  commands: 'commands',
58
60
  lazy: 'lazy',
@@ -204,6 +206,7 @@ function toProvisioningAPIFormat(device) {
204
206
  timezone: device.timezone,
205
207
  timestamp: device.timestamp,
206
208
  endpoint: device.endpoint,
209
+ polling: device.polling,
207
210
  transport: device.transport,
208
211
  attributes: device.active ? device.active.map(attributeToProvisioningAPIFormat) : undefined,
209
212
  lazy: device.lazy ? device.lazy.map(attributeToProvisioningAPIFormat) : undefined,
@@ -317,6 +320,14 @@ function handleRemoveDevice(req, res, next) {
317
320
  * This middleware handles updates in the provisioning devices. The only attribute
318
321
  */
319
322
  function handleUpdateDevice(req, res, next) {
323
+ function applyUpdatingHandler(device, callback) {
324
+ if (updatingHandler) {
325
+ updatingHandler(device, callback);
326
+ } else {
327
+ callback(null, device);
328
+ }
329
+ }
330
+
320
331
  if (req.body.device_id) {
321
332
  next(new errors.BadRequest("Can't change the ID of a preprovisioned device"));
322
333
  } else {
@@ -338,12 +349,19 @@ function handleUpdateDevice(req, res, next) {
338
349
  if (req.body.entity_name || req.body.entity_type) {
339
350
  isTypeOrNameUpdated = true;
340
351
  }
341
- deviceService.updateRegister(newDevice, isTypeOrNameUpdated, function handleDeviceUpdate(error) {
342
- if (error) {
343
- next(error);
344
- } else {
345
- res.status(204).json({});
346
- }
352
+ async.waterfall([apply(applyUpdatingHandler, newDevice)], function handleUpdating(
353
+ error,
354
+ newDeviceUpdated
355
+ ) {
356
+ deviceService.updateRegister(newDeviceUpdated, isTypeOrNameUpdated, function handleDeviceUpdate(
357
+ error
358
+ ) {
359
+ if (error) {
360
+ next(error);
361
+ } else {
362
+ res.status(204).json({});
363
+ }
364
+ });
347
365
  });
348
366
  } else {
349
367
  next(new errors.DeviceNotFound(req.params.deviceId));
@@ -392,6 +410,10 @@ function setProvisioningHandler(newHandler) {
392
410
  provisioningHandler = newHandler;
393
411
  }
394
412
 
413
+ function setUpdatingHandler(newHandler) {
414
+ updatingHandler = newHandler;
415
+ }
416
+
395
417
  function setRemoveDeviceHandler(newHandler) {
396
418
  removeDeviceHandler = newHandler;
397
419
  }
@@ -409,6 +431,7 @@ function clear(callback) {
409
431
  exports.setConfiguration = setConfiguration;
410
432
  exports.loadContextRoutes = intoTrans(context, loadContextRoutes);
411
433
  exports.setProvisioningHandler = intoTrans(context, setProvisioningHandler);
434
+ exports.setUpdatingHandler = intoTrans(context, setUpdatingHandler);
412
435
  exports.setRemoveDeviceHandler = intoTrans(context, setRemoveDeviceHandler);
413
436
  exports.addDeviceProvisionMiddleware = addDeviceProvisionMiddleware;
414
437
  exports.clear = clear;
@@ -31,6 +31,7 @@ const domainUtils = require('../common/domain');
31
31
  const middlewares = require('../common/genericMiddleware');
32
32
  const intoTrans = domainUtils.intoTrans;
33
33
  const deviceProvisioning = require('./deviceProvisioningServer');
34
+ const deviceUpdating = require('./deviceProvisioningServer');
34
35
  const groupProvisioning = require('./deviceGroupAdministrationServer');
35
36
  const logger = require('logops');
36
37
  const context = {
@@ -116,6 +117,7 @@ exports.setNotificationHandler = intoTrans(context, contextServer.setNotificatio
116
117
  exports.setConfigurationHandler = intoTrans(context, groupProvisioning.setConfigurationHandler);
117
118
  exports.setRemoveConfigurationHandler = intoTrans(context, groupProvisioning.setRemoveConfigurationHandler);
118
119
  exports.setProvisioningHandler = intoTrans(context, deviceProvisioning.setProvisioningHandler);
120
+ exports.setUpdatingHandler = intoTrans(context, deviceUpdating.setUpdatingHandler);
119
121
  exports.setRemoveDeviceHandler = intoTrans(context, deviceProvisioning.setRemoveDeviceHandler);
120
122
  exports.addDeviceProvisionMiddleware = deviceProvisioning.addDeviceProvisionMiddleware;
121
123
  exports.addConfigurationProvisionMiddleware = groupProvisioning.addConfigurationProvisionMiddleware;
@@ -35,7 +35,7 @@ const context = {
35
35
  op: 'IoTAgentNGSI.RestUtils'
36
36
  };
37
37
  const _ = require('underscore');
38
- const request = require('request');
38
+ const request = require('../../request-shim');
39
39
  const async = require('async');
40
40
  const apply = async.apply;
41
41
  const ngsiService = require('../ngsi/ngsiService');
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "iotagent-node-lib",
3
3
  "license": "AGPL-3.0-only",
4
4
  "description": "IoT Agent library to interface with NGSI Context Broker",
5
- "version": "2.18.0",
5
+ "version": "2.19.0",
6
6
  "homepage": "https://github.com/telefonicaid/iotagent-node-lib",
7
7
  "keywords": [
8
8
  "fiware",
@@ -36,7 +36,7 @@
36
36
  "lint:text": "textlint '*.md' 'doc/*.md'",
37
37
  "prettier": "prettier --config .prettierrc.json --write '**/**/**/**/*.js' '**/**/**/*.js' '**/**/*.js' '**/*.js' '*.js'",
38
38
  "prettier:text": "prettier 'README.md' 'doc/*.md' 'doc/**/*.md' --no-config --tab-width 4 --print-width 120 --write --prose-wrap always",
39
- "test": "nyc --reporter=text mocha --recursive 'test/**/*.js' --reporter spec --timeout 5000 --ui bdd --exit --color true",
39
+ "test": "nyc --reporter=text mocha --recursive 'test/**/*.js' --reporter spec --timeout 8000 --ui bdd --exit --color true",
40
40
  "test:expression": "nyc --reporter=text mocha --recursive 'test/unit/expressions/*.js' --reporter spec --timeout 5000 --ui bdd --exit --color true",
41
41
  "test:multientity": "nyc --reporter=text mocha --recursive 'test/unit/ngsiv2/plugins/multientity-plugin_test.js' --reporter spec --timeout 5000 --ui bdd --exit --color true",
42
42
  "test:debug": "mocha --recursive 'test/**/*.js' --reporter spec --inspect-brk --timeout 30000 --ui bdd --exit",
@@ -50,16 +50,16 @@
50
50
  "body-parser": "~1.19.0",
51
51
  "command-shell-lib": "1.0.0",
52
52
  "express": "~4.16.4",
53
+ "got": "~11.8.2",
53
54
  "jexl": "2.3.0",
54
55
  "jison": "0.4.18",
55
- "logops": "2.1.0",
56
+ "logops": "2.1.2",
56
57
  "moment": "~2.24.0",
57
58
  "moment-timezone": "~0.5.25",
58
59
  "mongodb": "3.6.8",
59
60
  "mongoose": "5.7.7",
60
61
  "mu2": "~0.5.20",
61
62
  "query-string": "6.5.0",
62
- "request": "2.88.0",
63
63
  "revalidator": "~0.3.1",
64
64
  "underscore": "~1.12.1",
65
65
  "uuid": "~3.3.2"
@@ -22,6 +22,7 @@
22
22
  */
23
23
 
24
24
  const fs = require('fs');
25
+ const request = require('../../lib/fiware-iotagent-lib').request;
25
26
 
26
27
  function readExampleFile(name, raw) {
27
28
  let text = null;
@@ -36,3 +37,4 @@ function readExampleFile(name, raw) {
36
37
  }
37
38
 
38
39
  exports.readExampleFile = readExampleFile;
40
+ exports.request = request;