iotagent-node-lib 2.24.0 → 2.26.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 (58) hide show
  1. package/.github/workflows/ci.yml +6 -7
  2. package/config.js +6 -1
  3. package/doc/advanced-topics.md +23 -1
  4. package/doc/apiary/iotagent.apib +5 -5
  5. package/doc/deprecated.md +13 -7
  6. package/doc/expressionLanguage.md +44 -34
  7. package/doc/getting-started.md +1 -1
  8. package/doc/howto.md +8 -0
  9. package/doc/installationguide.md +18 -0
  10. package/doc/usermanual.md +77 -0
  11. package/examples/TTOpen-service.json +1 -1
  12. package/lib/commonConfig.js +15 -1
  13. package/lib/constants.js +1 -0
  14. package/lib/fiware-iotagent-lib.js +3 -1
  15. package/lib/jexlTranformsMap.js +12 -1
  16. package/lib/plugins/jexlParser.js +2 -2
  17. package/lib/services/devices/deviceService.js +20 -1
  18. package/lib/services/devices/devices-NGSI-v2.js +3 -1
  19. package/lib/services/devices/registrationUtils.js +21 -1
  20. package/lib/services/northBound/contextServer-NGSI-LD.js +221 -38
  21. package/lib/services/northBound/contextServer.js +14 -1
  22. package/lib/services/northBound/northboundServer.js +1 -0
  23. package/lib/services/northBound/restUtils.js +3 -5
  24. package/lib/templates/deviceGroup.json +1 -1
  25. package/package.json +2 -2
  26. package/test/unit/examples/deviceProvisioningRequests/provisionNewDeviceEmpty.json +43 -0
  27. package/test/unit/examples/mongoCollections/configurations.json +3 -3
  28. package/test/unit/expressions/jexlExpression-test.js +29 -8
  29. package/test/unit/general/deviceService-test.js +31 -29
  30. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +26 -25
  31. package/test/unit/mongodb/mongodb-group-registry-test.js +3 -3
  32. package/test/unit/mongodb/mongodb-registry-test.js +30 -21
  33. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent1.json +24 -14
  34. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent2.json +25 -15
  35. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgent4.json +15 -5
  36. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +9 -1
  37. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommandsAndLazy.json +32 -0
  38. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice.json +12 -1
  39. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDevice2.json +9 -1
  40. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup.json +12 -1
  41. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup2.json +12 -1
  42. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerProvisionedDeviceWithGroup3.json +9 -1
  43. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateCommands1.json +10 -2
  44. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent1.json +11 -1
  45. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/updateIoTAgent2.json +12 -1
  46. package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin30.json +11 -0
  47. package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +18 -4
  48. package/test/unit/ngsi-ld/general/deviceService-test.js +31 -29
  49. package/test/unit/ngsi-ld/general/startup-test.js +17 -2
  50. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +1 -12
  51. package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +249 -0
  52. package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +171 -0
  53. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +0 -3
  54. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityJexlExpressionPlugin1.json +22 -0
  55. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +21 -6
  56. package/test/unit/ngsiv2/general/deviceService-test.js +25 -23
  57. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +58 -0
  58. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +25 -6
@@ -29,6 +29,7 @@ const iotAgentLib = require('../../../../lib/fiware-iotagent-lib');
29
29
  const utils = require('../../../tools/utils');
30
30
  const request = utils.request;
31
31
  const should = require('should');
32
+ const nock = require('nock');
32
33
 
33
34
  let contextBrokerMock;
34
35
  const iotAgentConfig = {
@@ -47,6 +48,50 @@ const iotAgentConfig = {
47
48
  providerUrl: 'http://smartgondor.com'
48
49
  };
49
50
 
51
+
52
+ const iotAgentConfigWithLimitedSupport = {
53
+ contextBroker: {
54
+ host: '192.168.1.1',
55
+ port: '1026',
56
+ ngsiVersion: 'ld',
57
+ jsonLdContext: 'http://context.json-ld'
58
+ },
59
+ server: {
60
+ port: 4041,
61
+ ldSupport : {
62
+ null: false,
63
+ datasetId: false
64
+ }
65
+ },
66
+ types: {
67
+ Robot: {
68
+ commands: [
69
+ {
70
+ name: 'position',
71
+ type: 'Array'
72
+ },
73
+ {
74
+ name: 'orientation',
75
+ type: 'Array'
76
+ }
77
+ ],
78
+ lazy: [],
79
+ staticAttributes: [],
80
+ active: []
81
+ }
82
+ },
83
+ service: 'smartgondor',
84
+ subservice: 'gardens',
85
+ providerUrl: 'http://smartgondor.com'
86
+ };
87
+
88
+ const device = {
89
+ id: 'r2d2',
90
+ type: 'Robot',
91
+ service: 'smartgondor'
92
+ };
93
+
94
+
50
95
  describe('NGSI-LD - Unsupported Endpoints', function () {
51
96
  beforeEach(function (done) {
52
97
  iotAgentLib.activate(iotAgentConfig, function () {
@@ -107,5 +152,131 @@ describe('NGSI-LD - Unsupported Endpoints', function () {
107
152
  done();
108
153
  });
109
154
  });
155
+
156
+
157
+ it('PUT /entities/<entity-id> includes an NGSI-LD Null should return a valid NSGI-LD error message', function (done) {
158
+ const options = {
159
+ url:
160
+ 'http://localhost:' +
161
+ iotAgentConfig.server.port +
162
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:entity/attrs/att',
163
+ method: 'PUT',
164
+ json: {
165
+ "value": "urn:ngsi-ld:null"
166
+ },
167
+ headers: {
168
+ 'fiware-service': 'smartgondor',
169
+ 'content-type': 'application/ld+json'
170
+ }
171
+ };
172
+ request(options, function (error, response, body) {
173
+ response.statusCode.should.equal(400);
174
+ done();
175
+ });
176
+ });
177
+ });
178
+ });
179
+
180
+ describe('NGSI-LD - Limiting Support', function () {
181
+ beforeEach(function (done) {
182
+ contextBrokerMock = nock('http://192.168.1.1:1026')
183
+ .matchHeader('fiware-service', 'smartgondor')
184
+ .post(
185
+ '/ngsi-ld/v1/csourceRegistrations/',
186
+ utils.readExampleFile(
187
+ './test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json'
188
+ )
189
+ )
190
+ .reply(201, null, { Location: '/ngsi-ld/v1/csourceRegistrations/6319a7f5254b05844116584d' });
191
+
192
+ contextBrokerMock
193
+ .matchHeader('fiware-service', 'smartgondor')
194
+ .post('/ngsi-ld/v1/entityOperations/upsert/')
195
+ .reply(204);
196
+
197
+
198
+ iotAgentLib.activate(iotAgentConfigWithLimitedSupport, function () {
199
+ iotAgentLib.clearAll(function () {
200
+ done();
201
+ });
202
+ });
203
+ });
204
+
205
+ afterEach(function (done) {
206
+ iotAgentLib.clearAll(function () {
207
+ iotAgentLib.deactivate(done);
208
+ });
110
209
  });
210
+
211
+ describe('When sending sending an NGSI-LD Null when nulls are unsupported', function () {
212
+ it('should return a valid NSGI-LD error message', function (done) {
213
+ const options = {
214
+ url:
215
+ 'http://localhost:' +
216
+ iotAgentConfig.server.port +
217
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs/position',
218
+ method: 'PATCH',
219
+ json: {
220
+ "value": "urn:ngsi-ld:null"
221
+ },
222
+ headers: {
223
+ 'fiware-service': 'smartgondor',
224
+ 'content-type': 'application/ld+json'
225
+ }
226
+ };
227
+
228
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
229
+ callback(null, {});
230
+ });
231
+
232
+ iotAgentLib.register(device, function (error) {
233
+ request(options, function (error, response, body) {
234
+ response.statusCode.should.equal(400);
235
+ done();
236
+ });
237
+ });
238
+
239
+ });
240
+ });
241
+ describe('When sending a payload including a datasetId when datasetIds are unsupported ', function () {
242
+ it('should return a valid NSGI-LD error message', function (done) {
243
+ const options = {
244
+ url:
245
+ 'http://localhost:' +
246
+ iotAgentConfig.server.port +
247
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs/',
248
+ method: 'PATCH',
249
+ json: {
250
+ position: [
251
+ {
252
+ type: 'Property',
253
+ value: [1, 2, 3],
254
+ datasetId: 'urn:ngsi-ld:this'
255
+ },
256
+ {
257
+ type: 'Property',
258
+ value: [28, -104, 23],
259
+ datasetId: 'urn:ngsi-ld:that'
260
+ }
261
+ ]
262
+ },
263
+ headers: {
264
+ 'fiware-service': 'smartgondor',
265
+ 'content-type': 'application/ld+json'
266
+ }
267
+ };
268
+
269
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
270
+ callback(null, {});
271
+ });
272
+
273
+ iotAgentLib.register(device, function (error) {
274
+ request(options, function (error, response, body) {
275
+ response.statusCode.should.equal(400);
276
+ done();
277
+ });
278
+ });
279
+
280
+ });
281
+ });
111
282
  });
@@ -65,7 +65,6 @@ const optionsCreationDefault = {
65
65
  services: [
66
66
  {
67
67
  apikey: 'default-test',
68
- cbroker: 'http://orion:1026',
69
68
  entity_type: 'Device',
70
69
  resource: '/iot/default',
71
70
  attributes: [
@@ -90,7 +89,6 @@ const optionsCreationV2 = {
90
89
  services: [
91
90
  {
92
91
  apikey: 'v2-test',
93
- cbroker: 'http://orion:1026',
94
92
  ngsiVersion: 'v2',
95
93
  entity_type: 'Device',
96
94
  resource: '/iot/v2',
@@ -117,7 +115,6 @@ const optionsCreationLD = {
117
115
  services: [
118
116
  {
119
117
  apikey: 'ld-test',
120
- cbroker: 'http://orion:1026',
121
118
  entity_type: 'Device',
122
119
  ngsiVersion: 'ld',
123
120
  resource: '/iot/ld',
@@ -0,0 +1,22 @@
1
+ {
2
+ "actionType":"update",
3
+ "entities":[
4
+ {
5
+ "id":"lightPseudo:id",
6
+ "type":"Light",
7
+ "pressure":{
8
+ "type":"Number",
9
+ "value":60
10
+ }
11
+ },
12
+ {
13
+ "id":"Ligth:mymulti",
14
+ "type":"myType",
15
+ "pressure":{
16
+ "type":"Number",
17
+ "value":90
18
+ }
19
+
20
+ }
21
+ ]
22
+ }
@@ -437,16 +437,31 @@ describe('Java expression language (JEXL) based transformations plugin', functio
437
437
  const values = [
438
438
  {
439
439
  name: 'p',
440
- type: 'centigrades',
441
- value: '52'
440
+ type: 'Number',
441
+ value: 1040
442
442
  }
443
443
  ];
444
444
 
445
- it('should apply the expression before sending the values', function (done) {
445
+ beforeEach(function () {
446
+ nock.cleanAll();
447
+
448
+ contextBrokerMock = nock('http://192.168.1.1:1026')
449
+ .matchHeader('fiware-service', 'smartgondor')
450
+ .matchHeader('fiware-servicepath', 'gardens')
451
+ .patch(
452
+ '/v2/entities/light1/attrs',
453
+ utils.readExampleFile(
454
+ './test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin30.json'
455
+ )
456
+ )
457
+ .query({ type: 'Light' })
458
+ .reply(204);
459
+ });
460
+
461
+ it('should ignore the expression and send the values', function (done) {
446
462
  iotAgentLib.update('light1', 'LightError', '', values, function (error) {
447
- should.exist(error);
448
- error.name.should.equal('INVALID_EXPRESSION');
449
- error.code.should.equal(400);
463
+ should.not.exist(error);
464
+ contextBrokerMock.done();
450
465
  done();
451
466
  });
452
467
  });
@@ -137,7 +137,7 @@ const groupCreation = {
137
137
  apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732',
138
138
  entity_type: 'TheLightType',
139
139
  trust: '8970A9078A803H3BL98PINEQRW8342HBAMS',
140
- cbHost: 'http://unexistentHost:1026',
140
+ cbHost: 'http://192.168.1.1:1026',
141
141
  commands: [],
142
142
  lazy: [],
143
143
  attributes: [
@@ -187,7 +187,7 @@ describe('NGSI-v2 - Device Service: utils', function () {
187
187
  // This mock does not check the payload since the aim of the test is not to verify
188
188
  // device provisioning functionality. Appropriate verification is done in tests under
189
189
  // provisioning folder
190
- contextBrokerMock = nock('http://unexistenthost:1026')
190
+ contextBrokerMock = nock('http://192.168.1.1:1026')
191
191
  .matchHeader('fiware-service', 'testservice')
192
192
  .matchHeader('fiware-servicepath', '/testingPath')
193
193
  .post('/v2/entities?options=upsert')
@@ -217,7 +217,7 @@ describe('NGSI-v2 - Device Service: utils', function () {
217
217
  // This mock does not check the payload since the aim of the test is not to verify
218
218
  // device provisioning functionality. Appropriate verification is done in tests under
219
219
  // provisioning folder
220
- contextBrokerMock = nock('http://unexistenthost:1026')
220
+ contextBrokerMock = nock('http://192.168.1.1:1026')
221
221
  .matchHeader('fiware-service', 'testservice')
222
222
  .matchHeader('fiware-servicepath', '/testingPath')
223
223
  .post('/v2/entities?options=upsert')
@@ -229,32 +229,34 @@ describe('NGSI-v2 - Device Service: utils', function () {
229
229
  });
230
230
 
231
231
  it('should register the device and return it', function (done) {
232
- iotAgentLib.retrieveDevice('UNEXISTENT_DEV', '801230BJKL23Y9090DSFL123HJK09H324HV8732', function (
233
- error,
234
- device
235
- ) {
236
- should.not.exist(error);
237
- should.exist(device);
232
+ iotAgentLib.retrieveDevice(
233
+ 'UNEXISTENT_DEV',
234
+ '801230BJKL23Y9090DSFL123HJK09H324HV8732',
235
+ function (error, device) {
236
+ should.not.exist(error);
237
+ should.exist(device);
238
238
 
239
- device.id.should.equal('UNEXISTENT_DEV');
240
- should.exist(device.protocol);
241
- device.protocol.should.equal('MQTT_UL');
242
- done();
243
- });
239
+ device.id.should.equal('UNEXISTENT_DEV');
240
+ should.exist(device.protocol);
241
+ device.protocol.should.equal('MQTT_UL');
242
+ done();
243
+ }
244
+ );
244
245
  });
245
246
  });
246
247
 
247
248
  describe('When an unexisting device tries to be retrieved for an unexisting APIKey', function () {
248
249
  it('should raise an error', function (done) {
249
- iotAgentLib.retrieveDevice('UNEXISTENT_DEV_AND_GROUP', 'H2332Y909DSF3H346yh20JK092', function (
250
- error,
251
- device
252
- ) {
253
- should.exist(error);
254
- error.name.should.equal('DEVICE_GROUP_NOT_FOUND');
255
- should.not.exist(device);
256
- done();
257
- });
250
+ iotAgentLib.retrieveDevice(
251
+ 'UNEXISTENT_DEV_AND_GROUP',
252
+ 'H2332Y909DSF3H346yh20JK092',
253
+ function (error, device) {
254
+ should.exist(error);
255
+ error.name.should.equal('DEVICE_GROUP_NOT_FOUND');
256
+ should.not.exist(device);
257
+ done();
258
+ }
259
+ );
258
260
  });
259
261
  });
260
262
  });
@@ -540,6 +540,26 @@ const iotAgentConfig = {
540
540
  }
541
541
  ],
542
542
  explicitAttrs: '[ "attr1", "attr2" ]'
543
+ },
544
+ LightMultiDefault: {
545
+ commands: [],
546
+ type: 'Light',
547
+ lazy: [],
548
+ active: [
549
+ {
550
+ object_id: 'p',
551
+ name: 'pressure',
552
+ type: 'Number',
553
+ entity_type: 'myType',
554
+ entity_name: 'Ligth:mymulti'
555
+ },
556
+ {
557
+ object_id: 'q',
558
+ name: 'pressure',
559
+ type: 'Number'
560
+ }
561
+ ],
562
+ expressionLanguage: 'jexl'
543
563
  }
544
564
  },
545
565
  service: 'smartgondor',
@@ -1356,6 +1376,44 @@ describe('NGSI-v2 - Multi-entity plugin', function () {
1356
1376
  });
1357
1377
  }
1358
1378
  );
1379
+
1380
+ describe('When pseudo-multientity device is provisioned (entity_type and default entity_id)', function () {
1381
+ // Case: Expression which results is sent as a new attribute
1382
+ const values = [
1383
+ {
1384
+ name: 'p',
1385
+ type: 'Number',
1386
+ value: 90
1387
+ },
1388
+ {
1389
+ name: 'q',
1390
+ type: 'Number',
1391
+ value: 60
1392
+ }
1393
+ ];
1394
+
1395
+ beforeEach(function () {
1396
+ nock.cleanAll();
1397
+ contextBrokerMock = nock('http://192.168.1.1:1026')
1398
+ .matchHeader('fiware-service', 'smartgondor')
1399
+ .matchHeader('fiware-servicepath', 'gardens')
1400
+ .post(
1401
+ '/v2/op/update',
1402
+ utils.readExampleFile(
1403
+ './test/unit/ngsiv2/examples/contextRequests/updateContextMultientityJexlExpressionPlugin1.json'
1404
+ )
1405
+ )
1406
+ .reply(204);
1407
+ });
1408
+
1409
+ it('should work without invalid expression error', function (done) {
1410
+ iotAgentLib.update('lightPseudo:id', 'LightMultiDefault', '', values, function (error) {
1411
+ should.not.exist(error);
1412
+ contextBrokerMock.done();
1413
+ done();
1414
+ });
1415
+ });
1416
+ });
1359
1417
  });
1360
1418
 
1361
1419
  describe('NGSI-v2 - Multi-entity plugin is executed before timestamp process plugin', function () {
@@ -450,7 +450,7 @@ describe('NGSI-v2 - Device provisioning API: Provision devices', function () {
450
450
  apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732',
451
451
  /*jshint camelcase: false */
452
452
  entity_type: 'MicroLights',
453
- cbroker: 'http://192.168.1.1:1026',
453
+ cbHost: 'http://192.168.1.1:1026',
454
454
  explicitAttrs: true
455
455
  }
456
456
  ]
@@ -516,7 +516,7 @@ describe('NGSI-v2 - Device provisioning API: Provision devices', function () {
516
516
  apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732',
517
517
  /*jshint camelcase: false */
518
518
  entity_type: 'MicroLights',
519
- cbroker: 'http://192.168.1.1:1026',
519
+ cbHost: 'http://192.168.1.1:1026',
520
520
  explicitAttrs: true,
521
521
  static_attributes: [
522
522
  {
@@ -589,7 +589,7 @@ describe('NGSI-v2 - Device provisioning API: Provision devices', function () {
589
589
  apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732',
590
590
  /*jshint camelcase: false */
591
591
  entity_type: 'MicroLights',
592
- cbroker: 'http://192.168.1.1:1026',
592
+ cbHost: 'http://192.168.1.1:1026',
593
593
  explicitAttrs: true
594
594
  }
595
595
  ]
@@ -656,7 +656,7 @@ describe('NGSI-v2 - Device provisioning API: Provision devices', function () {
656
656
  apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732',
657
657
  /*jshint camelcase: false */
658
658
  entity_type: 'MicroLights',
659
- cbroker: 'http://192.168.1.1:1026',
659
+ cbHost: 'http://192.168.1.1:1026',
660
660
  explicitAttrs: true,
661
661
  static_attributes: [
662
662
  {
@@ -726,7 +726,7 @@ describe('NGSI-v2 - Device provisioning API: Provision devices', function () {
726
726
  /*jshint camelcase: false */
727
727
  entity_type: 'MicroLights',
728
728
  entityNameExp: 'EntityNameByExp',
729
- cbroker: 'http://192.168.1.1:1026'
729
+ cbHost: 'http://192.168.1.1:1026'
730
730
  }
731
731
  ]
732
732
  },
@@ -790,7 +790,7 @@ describe('NGSI-v2 - Device provisioning API: Provision devices', function () {
790
790
  apikey: '801230BJKL23Y9090DSFL123HJK09H324HV8732',
791
791
  /*jshint camelcase: false */
792
792
  entity_type: 'MicroLights',
793
- cbroker: 'http://192.168.1.1:1026',
793
+ cbHost: 'http://192.168.1.1:1026',
794
794
  explicitAttrs: true,
795
795
  static_attributes: [
796
796
  {
@@ -1385,4 +1385,23 @@ describe('NGSI-v2 - Device provisioning API: Provision devices', function () {
1385
1385
  });
1386
1386
  });
1387
1387
  });
1388
+ describe('When a device provisioning request arrives to the Agent without device_id', function () {
1389
+ const options = {
1390
+ url: 'http://localhost:' + iotAgentConfig.server.port + '/iot/devices',
1391
+ headers: {
1392
+ 'fiware-service': 'smartgondor',
1393
+ 'fiware-servicepath': '/gardens'
1394
+ },
1395
+ method: 'POST',
1396
+ json: utils.readExampleFile('./test/unit/examples/deviceProvisioningRequests/provisionNewDeviceEmpty.json')
1397
+ };
1398
+
1399
+ it('should return a 400 error', function (done) {
1400
+ request(options, function (error, response, body) {
1401
+ should.not.exist(error);
1402
+ response.statusCode.should.equal(400);
1403
+ done();
1404
+ });
1405
+ });
1406
+ });
1388
1407
  });