iotagent-node-lib 2.21.0 → 2.24.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 (62) hide show
  1. package/.nyc_output/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +1 -0
  2. package/.nyc_output/processinfo/{76bc24ff-5fac-4b5a-997d-de2799342eb0.json → 33364de2-1199-4ec2-b33c-cae063ef8cc4.json} +1 -1
  3. package/.nyc_output/processinfo/index.json +1 -1
  4. package/CHANGES_NEXT_RELEASE +0 -1
  5. package/doc/Contribution.md +3 -3
  6. package/doc/advanced-topics.md +73 -9
  7. package/doc/api.md +7 -5
  8. package/doc/architecture.md +52 -5
  9. package/doc/expressionLanguage.md +18 -3
  10. package/doc/operations.md +8 -5
  11. package/doc/usermanual.md +18 -16
  12. package/docker/Mosquitto/Dockerfile +1 -1
  13. package/lib/errors.js +9 -1
  14. package/lib/model/Group.js +2 -1
  15. package/lib/model/dbConn.js +4 -0
  16. package/lib/plugins/bidirectionalData.js +104 -6
  17. package/lib/plugins/expressionPlugin.js +18 -7
  18. package/lib/plugins/multiEntity.js +42 -29
  19. package/lib/plugins/pluginUtils.js +17 -0
  20. package/lib/request-shim.js +2 -1
  21. package/lib/services/commands/commandService.js +29 -2
  22. package/lib/services/common/iotManagerService.js +2 -1
  23. package/lib/services/devices/deviceService.js +35 -9
  24. package/lib/services/groups/groupRegistryMongoDB.js +13 -12
  25. package/lib/services/ngsi/entities-NGSI-LD.js +7 -0
  26. package/lib/services/ngsi/entities-NGSI-v2.js +70 -11
  27. package/lib/services/ngsi/ngsiService.js +1 -1
  28. package/lib/services/northBound/contextServer-NGSI-LD.js +110 -15
  29. package/lib/services/northBound/contextServer-NGSI-v2.js +8 -3
  30. package/lib/services/northBound/contextServerUtils.js +9 -9
  31. package/lib/services/northBound/deviceProvisioningServer.js +2 -1
  32. package/lib/templates/createDevice.json +1 -2
  33. package/lib/templates/createDeviceLax.json +2 -3
  34. package/lib/templates/updateDevice.json +1 -2
  35. package/package.json +24 -24
  36. package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +14 -0
  37. package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +51 -0
  38. package/test/unit/mongodb/mongoDBUtils.js +2 -2
  39. package/test/unit/mongodb/mongodb-group-registry-test.js +25 -1
  40. package/test/unit/mongodb/mongodb-registry-test.js +51 -3
  41. package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +2 -1
  42. package/test/unit/ngsi-ld/examples/contextRequests/updateContextLanguageProperties1.json +15 -0
  43. package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithDatasetId.json +21 -0
  44. package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +17 -0
  45. package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +995 -27
  46. package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +112 -0
  47. package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +111 -0
  48. package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +221 -0
  49. package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +1 -1
  50. package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +8 -0
  51. package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +6 -0
  52. package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +12 -1
  53. package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +27 -0
  54. package/test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +19 -0
  55. package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +10 -8
  56. package/test/unit/ngsiv2/lazyAndCommands/command-test.js +106 -0
  57. package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +151 -0
  58. package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +115 -0
  59. package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +63 -0
  60. package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +60 -0
  61. package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +2 -1
  62. package/.nyc_output/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +0 -1
@@ -94,6 +94,10 @@ const iotAgentConfig = {
94
94
  {
95
95
  name: 'position',
96
96
  type: 'Array'
97
+ },
98
+ {
99
+ name: 'orientation',
100
+ type: 'Array'
97
101
  }
98
102
  ],
99
103
  lazy: [],
@@ -158,7 +162,114 @@ describe('NGSI-LD - Command functionalities', function () {
158
162
  });
159
163
  });
160
164
  });
161
- describe('When a command update arrives to the IoT Agent as Context Provider', function () {
165
+
166
+ describe('When multiple command updates via PATCH /attrs arrive to the IoT Agent as Context Provider', function () {
167
+ const options = {
168
+ url: 'http://localhost:' + iotAgentConfig.server.port + '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs',
169
+ method: 'PATCH',
170
+ json: {
171
+ orientation: {
172
+ type: 'Property',
173
+ value: [1, 2, 3]
174
+ },
175
+ position: {
176
+ type: 'Property',
177
+ value: [28, -104, 23]
178
+ }
179
+ },
180
+ headers: {
181
+ 'fiware-service': 'smartgondor',
182
+ 'content-type': 'application/ld+json'
183
+ }
184
+ };
185
+
186
+ beforeEach(function (done) {
187
+ logger.setLevel('ERROR');
188
+ iotAgentLib.register(device3, function (error) {
189
+ done();
190
+ });
191
+ });
192
+
193
+ it('should call the client handler once', function (done) {
194
+ let handlerCalled = 0;
195
+
196
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
197
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
198
+ type.should.equal(device3.type);
199
+ attributes[0].name.should.equal('position');
200
+ attributes[1].name.should.equal('orientation');
201
+ JSON.stringify(attributes[0].value).should.equal('[28,-104,23]');
202
+ JSON.stringify(attributes[1].value).should.equal('[1,2,3]');
203
+ handlerCalled++;
204
+ callback(null, {
205
+ id,
206
+ type,
207
+ attributes: [
208
+ {
209
+ name: 'position',
210
+ type: 'Array',
211
+ value: '[28, -104, 23]'
212
+ },
213
+ {
214
+ name: 'orientation',
215
+ type: 'Array',
216
+ value: '[1, 2, 3]'
217
+ }
218
+ ]
219
+ });
220
+ });
221
+
222
+ request(options, function (error, response, body) {
223
+ should.not.exist(error);
224
+ handlerCalled.should.equal(1);
225
+ done();
226
+ });
227
+ });
228
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
229
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
230
+ callback(null, {
231
+ id,
232
+ type,
233
+ attributes: [
234
+ {
235
+ name: 'position',
236
+ type: 'Array',
237
+ value: '[28, -104, 23]'
238
+ }
239
+ ]
240
+ });
241
+ });
242
+
243
+ request(options, function (error, response, body) {
244
+ should.not.exist(error);
245
+ done();
246
+ });
247
+ });
248
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
249
+ let serviceReceived = false;
250
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
251
+ serviceReceived = service === 'smartgondor';
252
+ callback(null, {
253
+ id,
254
+ type,
255
+ attributes: [
256
+ {
257
+ name: 'position',
258
+ type: 'Array',
259
+ value: '[28, -104, 23]'
260
+ }
261
+ ]
262
+ });
263
+ });
264
+
265
+ request(options, function (error, response, body) {
266
+ serviceReceived.should.equal(true);
267
+ done();
268
+ });
269
+ });
270
+ });
271
+
272
+ describe('When a command update PATCH attrs/attr-name arrives to the IoT Agent as Context Provider', function () {
162
273
  const options = {
163
274
  url:
164
275
  'http://localhost:' +
@@ -253,50 +364,907 @@ describe('NGSI-LD - Command functionalities', function () {
253
364
  });
254
365
  });
255
366
  });
256
- describe('When an update arrives from the south bound for a registered command', function () {
257
- beforeEach(function (done) {
258
- statusAttributeMock = nock('http://192.168.1.1:1026')
259
- .matchHeader('fiware-service', 'smartgondor')
260
- .post(
261
- '/ngsi-ld/v1/entityOperations/upsert/?options=update',
262
- utils.readExampleFile(
263
- './test/unit/ngsi-ld/examples/contextRequests/updateContextCommandFinish.json'
264
- )
265
- )
266
- .reply(204);
267
367
 
368
+ describe('When a sequential command with datasetId updates via PATCH /attrs/attr-name arrives to the IoT Agent', function () {
369
+ const options = {
370
+ url: 'http://localhost:' + iotAgentConfig.server.port + '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs',
371
+ method: 'PUT',
372
+ json: {
373
+ position: [
374
+ {
375
+ type: 'Property',
376
+ value: [1, 2, 3],
377
+ datasetId: 'urn:ngsi-ld:this'
378
+ },
379
+ {
380
+ type: 'Property',
381
+ value: [28, -104, 23],
382
+ datasetId: 'urn:ngsi-ld:that'
383
+ }
384
+ ]
385
+ },
386
+ headers: {
387
+ 'fiware-service': 'smartgondor',
388
+ 'content-type': 'application/ld+json'
389
+ }
390
+ };
391
+
392
+ beforeEach(function (done) {
393
+ logger.setLevel('ERROR');
268
394
  iotAgentLib.register(device3, function (error) {
269
395
  done();
270
396
  });
271
397
  });
272
398
 
273
- it('should update its value and status in the Context Broker', function (done) {
274
- iotAgentLib.setCommandResult('r2d2', 'Robot', '', 'position', '[72, 368, 1]', 'FINISHED', function (error) {
399
+ it('should call the client handler once including datasetId', function (done) {
400
+ let handlerCalled = 0;
401
+
402
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
403
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
404
+ type.should.equal(device3.type);
405
+ attributes[0].name.should.equal('position');
406
+ attributes[0].datasetId.should.equal('urn:ngsi-ld:this');
407
+ attributes[1].name.should.equal('position');
408
+ attributes[1].datasetId.should.equal('urn:ngsi-ld:that');
409
+ JSON.stringify(attributes[0].value).should.equal('[1,2,3]');
410
+ JSON.stringify(attributes[1].value).should.equal('[28,-104,23]');
411
+ handlerCalled++;
412
+ callback(null, {
413
+ id,
414
+ type,
415
+ attributes: [
416
+ {
417
+ name: 'position',
418
+ type: 'Array',
419
+ value: '[28, -104, 23]'
420
+ },
421
+ {
422
+ name: 'orientation',
423
+ type: 'Array',
424
+ value: '[1, 2, 3]'
425
+ }
426
+ ]
427
+ });
428
+ });
429
+
430
+ request(options, function (error, response, body) {
275
431
  should.not.exist(error);
276
- statusAttributeMock.done();
432
+ handlerCalled.should.equal(1);
433
+ done();
434
+ });
435
+ });
436
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
437
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
438
+ callback(null, {
439
+ id,
440
+ type,
441
+ attributes: [
442
+ {
443
+ name: 'position',
444
+ type: 'Array',
445
+ value: '[28, -104, 23]'
446
+ }
447
+ ]
448
+ });
449
+ });
450
+
451
+ request(options, function (error, response, body) {
452
+ should.not.exist(error);
453
+ done();
454
+ });
455
+ });
456
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
457
+ let serviceReceived = false;
458
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
459
+ serviceReceived = service === 'smartgondor';
460
+ callback(null, {
461
+ id,
462
+ type,
463
+ attributes: [
464
+ {
465
+ name: 'position',
466
+ type: 'Array',
467
+ value: '[28, -104, 23]'
468
+ }
469
+ ]
470
+ });
471
+ });
472
+
473
+ request(options, function (error, response, body) {
474
+ serviceReceived.should.equal(true);
277
475
  done();
278
476
  });
279
477
  });
280
478
  });
281
- describe('When an error command arrives from the south bound for a registered command', function () {
282
- beforeEach(function (done) {
283
- statusAttributeMock = nock('http://192.168.1.1:1026')
284
- .matchHeader('fiware-service', 'smartgondor')
285
- .post(
286
- '/ngsi-ld/v1/entityOperations/upsert/?options=update',
287
- utils.readExampleFile('./test/unit/ngsi-ld/examples/contextRequests/updateContextCommandError.json')
288
- )
289
- .reply(204);
290
479
 
480
+ describe('When a command update PATCH attrs/attr-name with datasetId arrives to the IoT Agent as Context Provider', function () {
481
+ const options = {
482
+ url:
483
+ 'http://localhost:' +
484
+ iotAgentConfig.server.port +
485
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs/position',
486
+ method: 'PATCH',
487
+ json: {
488
+ type: 'Property',
489
+ value: [28, -104, 23],
490
+ datasetId: 'urn:ngsi-ld:this'
491
+ },
492
+ headers: {
493
+ 'fiware-service': 'smartgondor',
494
+ 'content-type': 'application/ld+json'
495
+ }
496
+ };
497
+
498
+ beforeEach(function (done) {
499
+ logger.setLevel('ERROR');
291
500
  iotAgentLib.register(device3, function (error) {
292
501
  done();
293
502
  });
294
503
  });
295
504
 
296
- it('should update its status in the Context Broker', function (done) {
297
- iotAgentLib.setCommandResult('r2d2', 'Robot', '', 'position', 'Stalled', 'ERROR', function (error) {
505
+ it('should call the client handler once including datasetId', function (done) {
506
+ let handlerCalled = 0;
507
+
508
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
509
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
510
+ type.should.equal(device3.type);
511
+ attributes[0].name.should.equal('position');
512
+ attributes[0].datasetId.should.equal('urn:ngsi-ld:this');
513
+ JSON.stringify(attributes[0].value).should.equal('[28,-104,23]');
514
+ handlerCalled++;
515
+ callback(null, {
516
+ id,
517
+ type,
518
+ attributes: [
519
+ {
520
+ name: 'position',
521
+ type: 'Array',
522
+ value: '[28, -104, 23]'
523
+ }
524
+ ]
525
+ });
526
+ });
527
+
528
+ request(options, function (error, response, body) {
298
529
  should.not.exist(error);
299
- statusAttributeMock.done();
530
+ handlerCalled.should.equal(1);
531
+ done();
532
+ });
533
+ });
534
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
535
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
536
+ callback(null, {
537
+ id,
538
+ type,
539
+ attributes: [
540
+ {
541
+ name: 'position',
542
+ type: 'Array',
543
+ value: '[28, -104, 23]'
544
+ }
545
+ ]
546
+ });
547
+ });
548
+
549
+ request(options, function (error, response, body) {
550
+ should.not.exist(error);
551
+ done();
552
+ });
553
+ });
554
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
555
+ let serviceReceived = false;
556
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
557
+ serviceReceived = service === 'smartgondor';
558
+ callback(null, {
559
+ id,
560
+ type,
561
+ attributes: [
562
+ {
563
+ name: 'position',
564
+ type: 'Array',
565
+ value: '[28, -104, 23]'
566
+ }
567
+ ]
568
+ });
569
+ });
570
+
571
+ request(options, function (error, response, body) {
572
+ serviceReceived.should.equal(true);
573
+ done();
574
+ });
575
+ });
576
+ });
577
+
578
+ describe('When a command update PATCH attrs/attr-name with metadata arrives to the IoT Agent as Context Provider', function () {
579
+ const options = {
580
+ url:
581
+ 'http://localhost:' +
582
+ iotAgentConfig.server.port +
583
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs/position',
584
+ method: 'PATCH',
585
+ json: {
586
+ type: 'Property',
587
+ value: [28, -104, 23],
588
+ qos: 1
589
+ },
590
+ headers: {
591
+ 'fiware-service': 'smartgondor',
592
+ 'content-type': 'application/ld+json'
593
+ }
594
+ };
595
+
596
+ beforeEach(function (done) {
597
+ logger.setLevel('ERROR');
598
+ iotAgentLib.register(device3, function (error) {
599
+ done();
600
+ });
601
+ });
602
+
603
+ it('should call the client handler once including metadata', function (done) {
604
+ let handlerCalled = 0;
605
+
606
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
607
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
608
+ type.should.equal(device3.type);
609
+ attributes[0].name.should.equal('position');
610
+ attributes[0].metadata.qos.should.equal(1);
611
+ JSON.stringify(attributes[0].value).should.equal('[28,-104,23]');
612
+ handlerCalled++;
613
+ callback(null, {
614
+ id,
615
+ type,
616
+ attributes: [
617
+ {
618
+ name: 'position',
619
+ type: 'Array',
620
+ value: '[28, -104, 23]'
621
+ }
622
+ ]
623
+ });
624
+ });
625
+
626
+ request(options, function (error, response, body) {
627
+ should.not.exist(error);
628
+ handlerCalled.should.equal(1);
629
+ done();
630
+ });
631
+ });
632
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
633
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
634
+ callback(null, {
635
+ id,
636
+ type,
637
+ attributes: [
638
+ {
639
+ name: 'position',
640
+ type: 'Array',
641
+ value: '[28, -104, 23]'
642
+ }
643
+ ]
644
+ });
645
+ });
646
+
647
+ request(options, function (error, response, body) {
648
+ should.not.exist(error);
649
+ done();
650
+ });
651
+ });
652
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
653
+ let serviceReceived = false;
654
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
655
+ serviceReceived = service === 'smartgondor';
656
+ callback(null, {
657
+ id,
658
+ type,
659
+ attributes: [
660
+ {
661
+ name: 'position',
662
+ type: 'Array',
663
+ value: '[28, -104, 23]'
664
+ }
665
+ ]
666
+ });
667
+ });
668
+
669
+ request(options, function (error, response, body) {
670
+ serviceReceived.should.equal(true);
671
+ done();
672
+ });
673
+ });
674
+ });
675
+
676
+ describe('When a command overwrite PUT attrs/attr-name arrives to the IoT Agent as Context Provider', function () {
677
+ const options = {
678
+ url:
679
+ 'http://localhost:' +
680
+ iotAgentConfig.server.port +
681
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs/position',
682
+ method: 'PUT',
683
+ json: {
684
+ type: 'Property',
685
+ value: [28, -104, 23]
686
+ },
687
+ headers: {
688
+ 'fiware-service': 'smartgondor',
689
+ 'content-type': 'application/ld+json'
690
+ }
691
+ };
692
+
693
+ beforeEach(function (done) {
694
+ logger.setLevel('ERROR');
695
+ iotAgentLib.register(device3, function (error) {
696
+ done();
697
+ });
698
+ });
699
+
700
+ it('should call the client handler once', function (done) {
701
+ let handlerCalled = 0;
702
+
703
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
704
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
705
+ type.should.equal(device3.type);
706
+ attributes[0].name.should.equal('position');
707
+ JSON.stringify(attributes[0].value).should.equal('[28,-104,23]');
708
+ handlerCalled++;
709
+ callback(null, {
710
+ id,
711
+ type,
712
+ attributes: [
713
+ {
714
+ name: 'position',
715
+ type: 'Array',
716
+ value: '[28, -104, 23]'
717
+ }
718
+ ]
719
+ });
720
+ });
721
+
722
+ request(options, function (error, response, body) {
723
+ should.not.exist(error);
724
+ handlerCalled.should.equal(1);
725
+ done();
726
+ });
727
+ });
728
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
729
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
730
+ callback(null, {
731
+ id,
732
+ type,
733
+ attributes: [
734
+ {
735
+ name: 'position',
736
+ type: 'Array',
737
+ value: '[28, -104, 23]'
738
+ }
739
+ ]
740
+ });
741
+ });
742
+
743
+ request(options, function (error, response, body) {
744
+ should.not.exist(error);
745
+ done();
746
+ });
747
+ });
748
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
749
+ let serviceReceived = false;
750
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
751
+ serviceReceived = service === 'smartgondor';
752
+ callback(null, {
753
+ id,
754
+ type,
755
+ attributes: [
756
+ {
757
+ name: 'position',
758
+ type: 'Array',
759
+ value: '[28, -104, 23]'
760
+ }
761
+ ]
762
+ });
763
+ });
764
+
765
+ request(options, function (error, response, body) {
766
+ serviceReceived.should.equal(true);
767
+ done();
768
+ });
769
+ });
770
+ });
771
+
772
+ describe('When a sequential command with datasetId overwrites via PUT /attrs/attr-name arrives to the IoT Agent', function () {
773
+ const options = {
774
+ url: 'http://localhost:' + iotAgentConfig.server.port + '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs',
775
+ method: 'PUT',
776
+ json: {
777
+ position: [
778
+ {
779
+ type: 'Property',
780
+ value: [1, 2, 3],
781
+ datasetId: 'urn:ngsi-ld:this'
782
+ },
783
+ {
784
+ type: 'Property',
785
+ value: [28, -104, 23],
786
+ datasetId: 'urn:ngsi-ld:that'
787
+ }
788
+ ]
789
+ },
790
+ headers: {
791
+ 'fiware-service': 'smartgondor',
792
+ 'content-type': 'application/ld+json'
793
+ }
794
+ };
795
+
796
+ beforeEach(function (done) {
797
+ logger.setLevel('ERROR');
798
+ iotAgentLib.register(device3, function (error) {
799
+ done();
800
+ });
801
+ });
802
+
803
+ it('should call the client handler once', function (done) {
804
+ let handlerCalled = 0;
805
+
806
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
807
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
808
+ type.should.equal(device3.type);
809
+ attributes[0].name.should.equal('position');
810
+ attributes[0].datasetId.should.equal('urn:ngsi-ld:this');
811
+ attributes[1].name.should.equal('position');
812
+ attributes[1].datasetId.should.equal('urn:ngsi-ld:that');
813
+ JSON.stringify(attributes[0].value).should.equal('[1,2,3]');
814
+ JSON.stringify(attributes[1].value).should.equal('[28,-104,23]');
815
+ handlerCalled++;
816
+ callback(null, {
817
+ id,
818
+ type,
819
+ attributes: [
820
+ {
821
+ name: 'position',
822
+ type: 'Array',
823
+ value: '[28, -104, 23]'
824
+ },
825
+ {
826
+ name: 'orientation',
827
+ type: 'Array',
828
+ value: '[1, 2, 3]'
829
+ }
830
+ ]
831
+ });
832
+ });
833
+
834
+ request(options, function (error, response, body) {
835
+ should.not.exist(error);
836
+ handlerCalled.should.equal(1);
837
+ done();
838
+ });
839
+ });
840
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
841
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
842
+ callback(null, {
843
+ id,
844
+ type,
845
+ attributes: [
846
+ {
847
+ name: 'position',
848
+ type: 'Array',
849
+ value: '[28, -104, 23]'
850
+ }
851
+ ]
852
+ });
853
+ });
854
+
855
+ request(options, function (error, response, body) {
856
+ should.not.exist(error);
857
+ done();
858
+ });
859
+ });
860
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
861
+ let serviceReceived = false;
862
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
863
+ serviceReceived = service === 'smartgondor';
864
+ callback(null, {
865
+ id,
866
+ type,
867
+ attributes: [
868
+ {
869
+ name: 'position',
870
+ type: 'Array',
871
+ value: '[28, -104, 23]'
872
+ }
873
+ ]
874
+ });
875
+ });
876
+
877
+ request(options, function (error, response, body) {
878
+ serviceReceived.should.equal(true);
879
+ done();
880
+ });
881
+ });
882
+ });
883
+
884
+ describe('When multiple command overwrites via PUT /attrs arrive to the IoT Agent as Context Provider', function () {
885
+ const options = {
886
+ url: 'http://localhost:' + iotAgentConfig.server.port + '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs',
887
+ method: 'PUT',
888
+ json: {
889
+ orientation: {
890
+ type: 'Property',
891
+ value: [1, 2, 3]
892
+ },
893
+ position: {
894
+ type: 'Property',
895
+ value: [28, -104, 23]
896
+ }
897
+ },
898
+ headers: {
899
+ 'fiware-service': 'smartgondor',
900
+ 'content-type': 'application/ld+json'
901
+ }
902
+ };
903
+
904
+ beforeEach(function (done) {
905
+ logger.setLevel('ERROR');
906
+ iotAgentLib.register(device3, function (error) {
907
+ done();
908
+ });
909
+ });
910
+
911
+ it('should call the client handler once', function (done) {
912
+ let handlerCalled = 0;
913
+
914
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
915
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
916
+ type.should.equal(device3.type);
917
+ attributes[0].name.should.equal('position');
918
+ attributes[1].name.should.equal('orientation');
919
+ JSON.stringify(attributes[0].value).should.equal('[28,-104,23]');
920
+ JSON.stringify(attributes[1].value).should.equal('[1,2,3]');
921
+ handlerCalled++;
922
+ callback(null, {
923
+ id,
924
+ type,
925
+ attributes: [
926
+ {
927
+ name: 'position',
928
+ type: 'Array',
929
+ value: '[28, -104, 23]'
930
+ },
931
+ {
932
+ name: 'orientation',
933
+ type: 'Array',
934
+ value: '[1, 2, 3]'
935
+ }
936
+ ]
937
+ });
938
+ });
939
+
940
+ request(options, function (error, response, body) {
941
+ should.not.exist(error);
942
+ handlerCalled.should.equal(1);
943
+ done();
944
+ });
945
+ });
946
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
947
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
948
+ callback(null, {
949
+ id,
950
+ type,
951
+ attributes: [
952
+ {
953
+ name: 'position',
954
+ type: 'Array',
955
+ value: '[28, -104, 23]'
956
+ }
957
+ ]
958
+ });
959
+ });
960
+
961
+ request(options, function (error, response, body) {
962
+ should.not.exist(error);
963
+ done();
964
+ });
965
+ });
966
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
967
+ let serviceReceived = false;
968
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
969
+ serviceReceived = service === 'smartgondor';
970
+ callback(null, {
971
+ id,
972
+ type,
973
+ attributes: [
974
+ {
975
+ name: 'position',
976
+ type: 'Array',
977
+ value: '[28, -104, 23]'
978
+ }
979
+ ]
980
+ });
981
+ });
982
+
983
+ request(options, function (error, response, body) {
984
+ serviceReceived.should.equal(true);
985
+ done();
986
+ });
987
+ });
988
+ });
989
+
990
+ describe('When a command overwrite PUT attrs/attr-name with datasetId arrives to the IoT Agent as Context Provider', function () {
991
+ const options = {
992
+ url:
993
+ 'http://localhost:' +
994
+ iotAgentConfig.server.port +
995
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs/position',
996
+ method: 'PUT',
997
+ json: {
998
+ type: 'Property',
999
+ value: [28, -104, 23],
1000
+ datasetId: 'urn:ngsi-ld:this'
1001
+ },
1002
+ headers: {
1003
+ 'fiware-service': 'smartgondor',
1004
+ 'content-type': 'application/ld+json'
1005
+ }
1006
+ };
1007
+
1008
+ beforeEach(function (done) {
1009
+ logger.setLevel('ERROR');
1010
+ iotAgentLib.register(device3, function (error) {
1011
+ done();
1012
+ });
1013
+ });
1014
+
1015
+ it('should call the client handler once', function (done) {
1016
+ let handlerCalled = 0;
1017
+
1018
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
1019
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
1020
+ type.should.equal(device3.type);
1021
+ attributes[0].name.should.equal('position');
1022
+ attributes[0].datasetId.should.equal('urn:ngsi-ld:this');
1023
+ JSON.stringify(attributes[0].value).should.equal('[28,-104,23]');
1024
+ handlerCalled++;
1025
+ callback(null, {
1026
+ id,
1027
+ type,
1028
+ attributes: [
1029
+ {
1030
+ name: 'position',
1031
+ type: 'Array',
1032
+ value: '[28, -104, 23]'
1033
+ }
1034
+ ]
1035
+ });
1036
+ });
1037
+
1038
+ request(options, function (error, response, body) {
1039
+ should.not.exist(error);
1040
+ handlerCalled.should.equal(1);
1041
+ done();
1042
+ });
1043
+ });
1044
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
1045
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
1046
+ callback(null, {
1047
+ id,
1048
+ type,
1049
+ attributes: [
1050
+ {
1051
+ name: 'position',
1052
+ type: 'Array',
1053
+ value: '[28, -104, 23]'
1054
+ }
1055
+ ]
1056
+ });
1057
+ });
1058
+
1059
+ request(options, function (error, response, body) {
1060
+ should.not.exist(error);
1061
+ done();
1062
+ });
1063
+ });
1064
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
1065
+ let serviceReceived = false;
1066
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
1067
+ serviceReceived = service === 'smartgondor';
1068
+ callback(null, {
1069
+ id,
1070
+ type,
1071
+ attributes: [
1072
+ {
1073
+ name: 'position',
1074
+ type: 'Array',
1075
+ value: '[28, -104, 23]'
1076
+ }
1077
+ ]
1078
+ });
1079
+ });
1080
+
1081
+ request(options, function (error, response, body) {
1082
+ serviceReceived.should.equal(true);
1083
+ done();
1084
+ });
1085
+ });
1086
+ });
1087
+
1088
+ describe('When a command update PUT attrs/attr-name with metadata arrives to the IoT Agent as Context Provider', function () {
1089
+ const options = {
1090
+ url:
1091
+ 'http://localhost:' +
1092
+ iotAgentConfig.server.port +
1093
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs/position',
1094
+ method: 'PUT',
1095
+ json: {
1096
+ type: 'Property',
1097
+ value: [28, -104, 23],
1098
+ qos: 1
1099
+ },
1100
+ headers: {
1101
+ 'fiware-service': 'smartgondor',
1102
+ 'content-type': 'application/ld+json'
1103
+ }
1104
+ };
1105
+
1106
+ beforeEach(function (done) {
1107
+ logger.setLevel('ERROR');
1108
+ iotAgentLib.register(device3, function (error) {
1109
+ done();
1110
+ });
1111
+ });
1112
+
1113
+ it('should call the client handler once including metadata', function (done) {
1114
+ let handlerCalled = 0;
1115
+
1116
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
1117
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
1118
+ type.should.equal(device3.type);
1119
+ attributes[0].name.should.equal('position');
1120
+ attributes[0].metadata.qos.should.equal(1);
1121
+ JSON.stringify(attributes[0].value).should.equal('[28,-104,23]');
1122
+ handlerCalled++;
1123
+ callback(null, {
1124
+ id,
1125
+ type,
1126
+ attributes: [
1127
+ {
1128
+ name: 'position',
1129
+ type: 'Array',
1130
+ value: '[28, -104, 23]'
1131
+ }
1132
+ ]
1133
+ });
1134
+ });
1135
+
1136
+ request(options, function (error, response, body) {
1137
+ should.not.exist(error);
1138
+ handlerCalled.should.equal(1);
1139
+ done();
1140
+ });
1141
+ });
1142
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
1143
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
1144
+ callback(null, {
1145
+ id,
1146
+ type,
1147
+ attributes: [
1148
+ {
1149
+ name: 'position',
1150
+ type: 'Array',
1151
+ value: '[28, -104, 23]'
1152
+ }
1153
+ ]
1154
+ });
1155
+ });
1156
+
1157
+ request(options, function (error, response, body) {
1158
+ should.not.exist(error);
1159
+ done();
1160
+ });
1161
+ });
1162
+ it('should create the attribute with the "_status" prefix in the Context Broker', function (done) {
1163
+ let serviceReceived = false;
1164
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
1165
+ serviceReceived = service === 'smartgondor';
1166
+ callback(null, {
1167
+ id,
1168
+ type,
1169
+ attributes: [
1170
+ {
1171
+ name: 'position',
1172
+ type: 'Array',
1173
+ value: '[28, -104, 23]'
1174
+ }
1175
+ ]
1176
+ });
1177
+ });
1178
+
1179
+ request(options, function (error, response, body) {
1180
+ serviceReceived.should.equal(true);
1181
+ done();
1182
+ });
1183
+ });
1184
+ });
1185
+
1186
+ describe('When an update arrives from the south bound for a registered command', function () {
1187
+ beforeEach(function (done) {
1188
+ statusAttributeMock = nock('http://192.168.1.1:1026')
1189
+ .matchHeader('fiware-service', 'smartgondor')
1190
+ .post(
1191
+ '/ngsi-ld/v1/entityOperations/upsert/?options=update',
1192
+ utils.readExampleFile(
1193
+ './test/unit/ngsi-ld/examples/contextRequests/updateContextCommandFinish.json'
1194
+ )
1195
+ )
1196
+ .reply(204);
1197
+
1198
+ iotAgentLib.register(device3, function (error) {
1199
+ done();
1200
+ });
1201
+ });
1202
+
1203
+ it('should update its value and status in the Context Broker', function (done) {
1204
+ iotAgentLib.setCommandResult('r2d2', 'Robot', '', 'position', '[72, 368, 1]', 'FINISHED', function (error) {
1205
+ should.not.exist(error);
1206
+ statusAttributeMock.done();
1207
+ done();
1208
+ });
1209
+ });
1210
+ });
1211
+ describe('When an error command arrives from the south bound for a registered command', function () {
1212
+ beforeEach(function (done) {
1213
+ statusAttributeMock = nock('http://192.168.1.1:1026')
1214
+ .matchHeader('fiware-service', 'smartgondor')
1215
+ .post(
1216
+ '/ngsi-ld/v1/entityOperations/upsert/?options=update',
1217
+ utils.readExampleFile('./test/unit/ngsi-ld/examples/contextRequests/updateContextCommandError.json')
1218
+ )
1219
+ .reply(204);
1220
+
1221
+ iotAgentLib.register(device3, function (error) {
1222
+ done();
1223
+ });
1224
+ });
1225
+
1226
+ it('should update its status in the Context Broker', function (done) {
1227
+ iotAgentLib.setCommandResult('r2d2', 'Robot', '', 'position', 'Stalled', 'ERROR', function (error) {
1228
+ should.not.exist(error);
1229
+ statusAttributeMock.done();
1230
+ done();
1231
+ });
1232
+ });
1233
+ });
1234
+
1235
+ describe('When a query arrives to the IoT Agent with registered commands but no lazy attributes', function () {
1236
+ const options = {
1237
+ url:
1238
+ 'http://localhost:' +
1239
+ iotAgentConfig.server.port +
1240
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2',
1241
+ method: 'GET',
1242
+ headers: {
1243
+ 'fiware-service': 'smartgondor',
1244
+ 'content-type': 'application/ld+json'
1245
+ }
1246
+ };
1247
+
1248
+ beforeEach(function (done) {
1249
+ logger.setLevel('ERROR');
1250
+ iotAgentLib.register(device3, function (error) {
1251
+ done();
1252
+ });
1253
+ });
1254
+
1255
+ it('should return the a valid empty response', function (done) {
1256
+ iotAgentLib.setDataQueryHandler(function (id, type, service, subservice, attributes, callback) {
1257
+ should.exist(attributes);
1258
+ attributes.length.should.equal(0);
1259
+ callback(null, {
1260
+ id: 'urn:ngsi-ld:Robot:r2d2',
1261
+ type: 'Robot'
1262
+ });
1263
+ });
1264
+
1265
+ request(options, function (error, response, body) {
1266
+ should.not.exist(error);
1267
+ response.statusCode.should.equal(200);
300
1268
  done();
301
1269
  });
302
1270
  });