iotagent-node-lib 2.20.0 → 2.23.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 (68) hide show
  1. package/.nyc_output/6e3d7795-bf8c-4a50-bd2f-f8221f27aeae.json +1 -0
  2. package/.nyc_output/processinfo/6e3d7795-bf8c-4a50-bd2f-f8221f27aeae.json +1 -0
  3. package/.nyc_output/processinfo/index.json +1 -1
  4. package/.readthedocs.yml +3 -1
  5. package/CHANGES_NEXT_RELEASE +1 -0
  6. package/README.md +2 -2
  7. package/config +0 -0
  8. package/doc/Contribution.md +3 -3
  9. package/doc/advanced-topics.md +19 -8
  10. package/doc/api.md +14 -3
  11. package/doc/expressionLanguage.md +17 -0
  12. package/doc/northboundinteractions.md +40 -33
  13. package/doc/operations.md +8 -5
  14. package/doc/requirements.txt +4 -0
  15. package/{docs → doc}/roadmap.md +21 -6
  16. package/doc/usermanual.md +4 -4
  17. package/docker/Mosquitto/Dockerfile +28 -12
  18. package/docker/Mosquitto/Dockerfile.debian +38 -0
  19. package/docker/Mosquitto/Dockerfile.debian~ +23 -0
  20. package/docker/Mosquitto/README.md +8 -7
  21. package/docker/Mosquitto/startMosquitto.sh +8 -0
  22. package/lib/fiware-iotagent-lib.js +1 -0
  23. package/lib/jexlTranformsMap.js +3 -1
  24. package/lib/model/Group.js +2 -1
  25. package/lib/model/dbConn.js +4 -0
  26. package/lib/plugins/expressionPlugin.js +56 -21
  27. package/lib/plugins/multiEntity.js +43 -49
  28. package/lib/plugins/multiEntity.js_avg2 +343 -0
  29. package/lib/plugins/pluginUtils.js +16 -0
  30. package/lib/services/commands/commandService.js +29 -2
  31. package/lib/services/common/iotManagerService.js +2 -1
  32. package/lib/services/devices/deviceRegistryMemory.js +13 -2
  33. package/lib/services/devices/deviceRegistryMongoDB.js +15 -7
  34. package/lib/services/devices/deviceService.js +52 -16
  35. package/lib/services/groups/groupRegistryMongoDB.js +13 -12
  36. package/lib/services/ngsi/entities-NGSI-LD.js +13 -4
  37. package/lib/services/ngsi/entities-NGSI-v2.js +64 -13
  38. package/lib/services/ngsi/ngsiService.js +3 -3
  39. package/lib/services/northBound/contextServer-NGSI-LD.js +20 -1
  40. package/lib/services/northBound/contextServer-NGSI-v2.js +39 -30
  41. package/lib/services/northBound/contextServerUtils.js +10 -10
  42. package/lib/services/northBound/deviceProvisioningServer.js +4 -1
  43. package/lib/templates/createDevice.json +13 -2
  44. package/lib/templates/createDeviceLax.json +2 -3
  45. package/lib/templates/updateDevice.json +13 -2
  46. package/package.json +26 -26
  47. package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +14 -0
  48. package/test/unit/mongodb/mongoDBUtils.js +2 -2
  49. package/test/unit/mongodb/mongodb-group-registry-test.js +1 -1
  50. package/test/unit/mongodb/mongodb-registry-test.js +2 -3
  51. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +2 -1
  52. package/test/unit/ngsi-ld/examples/contextRequests/updateContextLanguageProperties1.json +15 -0
  53. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +315 -1
  54. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +112 -0
  55. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +1 -1
  56. package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +8 -0
  57. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +6 -0
  58. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +2 -0
  59. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +12 -0
  60. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +27 -0
  61. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +72 -9
  62. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +151 -0
  63. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +63 -0
  64. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js_avg2 +1224 -0
  65. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +60 -0
  66. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +2 -1
  67. package/.nyc_output/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +0 -1
  68. package/.nyc_output/processinfo/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +0 -1
@@ -0,0 +1,27 @@
1
+ {
2
+ "actionType": "update",
3
+ "entities": [
4
+ {
5
+ "id": "ws9b",
6
+ "type": "WeatherStation",
7
+ "pressure": {
8
+ "type": "Hgmm",
9
+ "value": "52"
10
+ }
11
+ },
12
+ {
13
+ "humidity": {
14
+ "type": "Percentage",
15
+ "value": "12",
16
+ "metadata": {
17
+ "unitCode": {
18
+ "type": "Text",
19
+ "value": "Hgmm"
20
+ }
21
+ }
22
+ },
23
+ "type": "Higrometer",
24
+ "id": "ws9b"
25
+ }
26
+ ]
27
+ }
@@ -271,7 +271,7 @@ const iotAgentConfig = {
271
271
  expression: "{coordinates: [lon,lat], type: 'Point'}"
272
272
  }
273
273
  ],
274
- explicitAttrs: '[ "location" ]'
274
+ explicitAttrs: ' location&&price ? [ "location", "price" ] : location ? [ "location" ] : []'
275
275
  },
276
276
  GPS4: {
277
277
  commands: [],
@@ -314,12 +314,14 @@ const iotAgentConfig = {
314
314
  type: 'number'
315
315
  },
316
316
  {
317
- name: 'location',
318
- type: 'geo:json',
319
- expression: "{coordinates: [lon,lat], type: 'Point'}"
317
+ object_id: 'theLocation',
318
+ name: 'mylocation',
319
+ type: 'geo:json'
320
320
  }
321
321
  ],
322
- explicitAttrs: '[ myattr ]'
322
+ explicitAttrs: "theLocation ? ['mylocation'] : []"
323
+ // #1267 this is not working:
324
+ //explicitAttrs: "theLocation ? [{object_id: 'theLocation'}] : []"
323
325
  },
324
326
  GPS6: {
325
327
  commands: [],
@@ -345,6 +347,30 @@ const iotAgentConfig = {
345
347
  }
346
348
  ],
347
349
  explicitAttrs: true
350
+ },
351
+ GPS7: {
352
+ commands: [],
353
+ type: 'GPS',
354
+ lazy: [],
355
+ static: [
356
+ {
357
+ name: 'color',
358
+ type: 'string',
359
+ value: 'blue'
360
+ }
361
+ ],
362
+ active: [
363
+ {
364
+ name: 'price',
365
+ type: 'number'
366
+ },
367
+ {
368
+ name: 'location',
369
+ type: 'geo:json',
370
+ expression: "{coordinates: [lon,lat], type: 'Point'}"
371
+ }
372
+ ],
373
+ explicitAttrs: '[ ]'
348
374
  }
349
375
  },
350
376
  service: 'smartgondor',
@@ -1160,9 +1186,9 @@ describe('Java expression language (JEXL) based transformations plugin', functio
1160
1186
  value: 13
1161
1187
  },
1162
1188
  {
1163
- name: 'myattr',
1164
- type: 'String',
1165
- value: 'location'
1189
+ name: 'theLocation',
1190
+ type: 'geo:json',
1191
+ value: { coordinates: [13, 52], type: 'Point' }
1166
1192
  },
1167
1193
  {
1168
1194
  name: 'TimeInstant',
@@ -1180,7 +1206,7 @@ describe('Java expression language (JEXL) based transformations plugin', functio
1180
1206
  .patch(
1181
1207
  '/v2/entities/gps1/attrs',
1182
1208
  utils.readExampleFile(
1183
- './test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin34.json'
1209
+ './test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json'
1184
1210
  )
1185
1211
  )
1186
1212
  .query({ type: 'GPS' })
@@ -1230,6 +1256,43 @@ describe('Java expression language (JEXL) based transformations plugin', functio
1230
1256
  });
1231
1257
  });
1232
1258
  });
1259
+
1260
+ describe('When there is an extra TimeInstant sent by the device to be removed by jexl expression with context but with empty explicitAttrs', function () {
1261
+ // Case: Expression which results is sent as a new attribute
1262
+ const values = [
1263
+ {
1264
+ name: 'lat',
1265
+ type: 'Number',
1266
+ value: 52
1267
+ },
1268
+ {
1269
+ name: 'lon',
1270
+ type: 'Number',
1271
+ value: 13
1272
+ },
1273
+ {
1274
+ name: 'myattr',
1275
+ type: 'String',
1276
+ value: 'location'
1277
+ },
1278
+ {
1279
+ name: 'TimeInstant',
1280
+ type: 'DateTime',
1281
+ value: '2015-08-05T07:35:01.468+00:00'
1282
+ }
1283
+ ];
1284
+
1285
+ beforeEach(function () {
1286
+ nock.cleanAll();
1287
+ });
1288
+
1289
+ it('should calculate them and remove non-explicitAttrs by jexl expression with context from the payload ', function (done) {
1290
+ iotAgentLib.update('gps1', 'GPS7', '', values, function (error) {
1291
+ should.not.exist(error);
1292
+ done();
1293
+ });
1294
+ });
1295
+ });
1233
1296
  });
1234
1297
 
1235
1298
  describe('Java expression language (JEXL) based transformations plugin - Timestamps', function () {
@@ -98,6 +98,18 @@ const iotAgentConfig = {
98
98
  lazy: [],
99
99
  staticAttributes: [],
100
100
  active: []
101
+ },
102
+ RobotExp: {
103
+ commands: [
104
+ {
105
+ name: 'positionExp',
106
+ type: 'Array',
107
+ expression: '[22]'
108
+ }
109
+ ],
110
+ lazy: [],
111
+ staticAttributes: [],
112
+ active: []
101
113
  }
102
114
  },
103
115
  deviceRegistry: {
@@ -122,6 +134,14 @@ const device3 = {
122
134
  subservice: 'gardens',
123
135
  polling: true
124
136
  };
137
+ const device4 = {
138
+ id: 'r2d4',
139
+ type: 'RobotExp',
140
+ service: 'smartgondor',
141
+ subservice: 'gardens',
142
+ polling: true,
143
+ expressionLanguage: 'jexl'
144
+ };
125
145
 
126
146
  describe('NGSI-v2 - Polling commands', function () {
127
147
  beforeEach(function (done) {
@@ -386,3 +406,134 @@ describe('NGSI-v2 - Polling commands', function () {
386
406
  });
387
407
  });
388
408
  });
409
+
410
+ describe('NGSI-v2 - Polling commands expressions', function () {
411
+ beforeEach(function (done) {
412
+ logger.setLevel('FATAL');
413
+
414
+ nock.cleanAll();
415
+
416
+ contextBrokerMock = nock('http://192.168.1.1:1026')
417
+ .matchHeader('fiware-service', 'smartgondor')
418
+ .matchHeader('fiware-servicepath', 'gardens')
419
+ .post('/v2/registrations')
420
+ .reply(201, null, { Location: '/v2/registrations/6319a7f5254b05844116584m' });
421
+
422
+ contextBrokerMock
423
+ .matchHeader('fiware-service', 'smartgondor')
424
+ .matchHeader('fiware-servicepath', 'gardens')
425
+ .post('/v2/entities?options=upsert')
426
+ .reply(204);
427
+
428
+ iotAgentConfig.pollingExpiration = 0;
429
+ iotAgentConfig.pollingDaemonFrequency = 0;
430
+ iotAgentLib.activate(iotAgentConfig, done);
431
+ });
432
+
433
+ afterEach(function (done) {
434
+ delete device4.registrationId;
435
+ iotAgentLib.clearAll(function () {
436
+ iotAgentLib.deactivate(function () {
437
+ mongoUtils.cleanDbs(function () {
438
+ nock.cleanAll();
439
+ iotAgentLib.setDataUpdateHandler();
440
+ iotAgentLib.setCommandHandler();
441
+ done();
442
+ });
443
+ });
444
+ });
445
+ });
446
+
447
+ describe('When a command update arrives to the IoT Agent for a device with polling', function () {
448
+ const options = {
449
+ url: 'http://localhost:' + iotAgentConfig.server.port + '/v2/op/update',
450
+ method: 'POST',
451
+ json: {
452
+ actionType: 'update',
453
+ entities: [
454
+ {
455
+ id: 'RobotExp:r2d4',
456
+ type: 'RobotExp',
457
+ positionExp: {
458
+ type: 'Array',
459
+ value: '[28, -104, 23]'
460
+ }
461
+ }
462
+ ]
463
+ },
464
+ headers: {
465
+ 'fiware-service': 'smartgondor',
466
+ 'fiware-servicepath': 'gardens'
467
+ }
468
+ };
469
+
470
+ beforeEach(function (done) {
471
+ statusAttributeMock = nock('http://192.168.1.1:1026')
472
+ .matchHeader('fiware-service', 'smartgondor')
473
+ .matchHeader('fiware-servicepath', 'gardens')
474
+ .patch(
475
+ '/v2/entities/RobotExp:r2d4/attrs?type=RobotExp',
476
+ utils.readExampleFile(
477
+ './test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json'
478
+ )
479
+ )
480
+ .reply(204);
481
+
482
+ iotAgentLib.register(device4, function (error) {
483
+ done();
484
+ });
485
+ });
486
+
487
+ it('should not call the client handler', function (done) {
488
+ let handlerCalled = false;
489
+
490
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
491
+ handlerCalled = true;
492
+ callback(null, {
493
+ id,
494
+ type,
495
+ attributes: [
496
+ {
497
+ name: 'positionExp',
498
+ type: 'Array',
499
+ value: '[28, -104, 23]'
500
+ }
501
+ ]
502
+ });
503
+ });
504
+
505
+ request(options, function (error, response, body) {
506
+ should.not.exist(error);
507
+ handlerCalled.should.equal(false);
508
+ done();
509
+ });
510
+ });
511
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
512
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
513
+ callback(null);
514
+ });
515
+
516
+ request(options, function (error, response, body) {
517
+ should.not.exist(error);
518
+ statusAttributeMock.done();
519
+ done();
520
+ });
521
+ });
522
+ it('should store the commands in the queue', function (done) {
523
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
524
+ callback(null);
525
+ });
526
+
527
+ request(options, function (error, response, body) {
528
+ iotAgentLib.commandQueue('smartgondor', 'gardens', 'r2d4', function (error, listCommands) {
529
+ should.not.exist(error);
530
+ listCommands.count.should.equal(1);
531
+ listCommands.commands[0].name.should.equal('positionExp');
532
+ listCommands.commands[0].type.should.equal('Array');
533
+ listCommands.commands[0].value[0].should.equal(22);
534
+ done();
535
+ });
536
+ });
537
+ });
538
+ });
539
+ });
@@ -215,6 +215,31 @@ const iotAgentConfig = {
215
215
  }
216
216
  ]
217
217
  },
218
+ WeatherStation9: {
219
+ commands: [],
220
+ type: 'WeatherStation',
221
+ name: 'ws9b',
222
+ lazy: [],
223
+ active: [
224
+ {
225
+ object_id: 'p',
226
+ name: 'pressure',
227
+ type: 'Hgmm'
228
+ },
229
+ {
230
+ object_id: 'h',
231
+ name: 'humidity',
232
+ type: 'Percentage',
233
+ entity_type: 'Higrometer',
234
+ metadata: {
235
+ unitCode: {
236
+ type: 'Text',
237
+ value: 'Hgmm'
238
+ }
239
+ }
240
+ }
241
+ ]
242
+ },
218
243
  WeatherStation8Jexl: {
219
244
  commands: [],
220
245
  type: 'WeatherStation',
@@ -581,6 +606,44 @@ describe('NGSI-v2 - Multi-entity plugin', function () {
581
606
  });
582
607
  });
583
608
 
609
+ describe('When an update comes for a multientity measurement based on entity_type', function () {
610
+ const values = [
611
+ {
612
+ name: 'p',
613
+ type: 'centigrades',
614
+ value: '52'
615
+ },
616
+ {
617
+ name: 'h',
618
+ type: 'Percentage',
619
+ value: '12'
620
+ }
621
+ ];
622
+
623
+ beforeEach(function () {
624
+ nock.cleanAll();
625
+
626
+ contextBrokerMock = nock('http://192.168.1.1:1026')
627
+ .matchHeader('fiware-service', 'smartgondor')
628
+ .matchHeader('fiware-servicepath', 'gardens')
629
+ .post(
630
+ '/v2/op/update',
631
+ utils.readExampleFile(
632
+ './test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json'
633
+ )
634
+ )
635
+ .reply(204);
636
+ });
637
+
638
+ it('should send two context elements, one for each entity', function (done) {
639
+ iotAgentLib.update('ws9b', 'WeatherStation9', '', values, function (error) {
640
+ should.not.exist(error);
641
+ contextBrokerMock.done();
642
+ done();
643
+ });
644
+ });
645
+ });
646
+
584
647
  describe('When an update comes for a multientity measurement with same attribute name', function () {
585
648
  const values = [
586
649
  {