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
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "@context": "http://context.json-ld",
3
3
  "endpoint": "http://smartgondor.com",
4
+ "contextSourceInfo":[
5
+ {
6
+ "key": "jsonldContext",
7
+ "value": "http://context.json-ld"
8
+ }
9
+ ],
4
10
  "information": [
5
11
  {
6
12
  "entities": [
@@ -9,10 +15,14 @@
9
15
  "type": "Light"
10
16
  }
11
17
  ],
12
- "properties": [
18
+ "propertyNames": [
13
19
  "pressure"
14
20
  ]
15
21
  }
16
22
  ],
23
+ "mode": "exclusive",
24
+ "operations": [
25
+ "retrieveOps"
26
+ ],
17
27
  "type": "ContextSourceRegistration"
18
28
  }
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "@context": "http://context.json-ld",
3
3
  "endpoint": "http://smartgondor.com",
4
+ "contextSourceInfo":[
5
+ {
6
+ "key": "jsonldContext",
7
+ "value": "http://context.json-ld"
8
+ }
9
+ ],
4
10
  "information": [
5
11
  {
6
12
  "entities": [
@@ -9,11 +15,16 @@
9
15
  "type": "TheLightType"
10
16
  }
11
17
  ],
12
- "properties": [
18
+ "propertyNames": [
13
19
  "luminance",
14
20
  "commandAttr"
15
21
  ]
16
22
  }
17
23
  ],
24
+ "mode": "exclusive",
25
+ "operations" : [
26
+ "retrieveOps",
27
+ "updateOps"
28
+ ],
18
29
  "type": "ContextSourceRegistration"
19
30
  }
@@ -0,0 +1,11 @@
1
+ [
2
+ {
3
+ "@context": "http://context.json-ld",
4
+ "pressure": {
5
+ "type": "Property",
6
+ "value": 52
7
+ },
8
+ "id": "urn:ngsi-ld:Light:light1",
9
+ "type": "Light"
10
+ }
11
+ ]
@@ -321,11 +321,25 @@ describe('NGSI-LD: JEXL', function () {
321
321
  }
322
322
  ];
323
323
 
324
- it('should apply the expression before sending the values', function (done) {
324
+ beforeEach(function () {
325
+ nock.cleanAll();
326
+
327
+ contextBrokerMock = nock('http://192.168.1.1:1026')
328
+ .matchHeader('fiware-service', 'smartgondor')
329
+ .matchHeader('fiware-servicepath', 'gardens')
330
+ .post(
331
+ '/ngsi-ld/v1/entityOperations/upsert/?options=update',
332
+ utils.readExampleFile(
333
+ './test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin30.json'
334
+ )
335
+ )
336
+ .reply(204);
337
+ });
338
+
339
+ it('should ignore the expression before sending the values', function (done) {
325
340
  iotAgentLib.update('light1', 'LightError', '', values, function (error) {
326
- should.exist(error);
327
- error.name.should.equal('INVALID_EXPRESSION');
328
- error.code.should.equal(400);
341
+ should.not.exist(error);
342
+ contextBrokerMock.done();
329
343
  done();
330
344
  });
331
345
  });
@@ -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,17 +187,17 @@ describe('NGSI-LD - 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
  .post('/ngsi-ld/v1/entityOperations/upsert/')
193
193
  .reply(204);
194
194
 
195
- async.series([request.bind(request, groupCreation), request.bind(request, deviceCreation)], function (
196
- error,
197
- results
198
- ) {
199
- done();
200
- });
195
+ async.series(
196
+ [request.bind(request, groupCreation), request.bind(request, deviceCreation)],
197
+ function (error, results) {
198
+ done();
199
+ }
200
+ );
201
201
  });
202
202
 
203
203
  it('should return the existing device', function (done) {
@@ -216,7 +216,7 @@ describe('NGSI-LD - Device Service: utils', function () {
216
216
  // This mock does not check the payload since the aim of the test is not to verify
217
217
  // device provisioning functionality. Appropriate verification is done in tests under
218
218
  // provisioning folder
219
- contextBrokerMock = nock('http://unexistenthost:1026')
219
+ contextBrokerMock = nock('http://192.168.1.1:1026')
220
220
  .matchHeader('fiware-service', 'testservice')
221
221
  .post('/ngsi-ld/v1/entityOperations/upsert/')
222
222
  .reply(204);
@@ -227,32 +227,34 @@ describe('NGSI-LD - Device Service: utils', function () {
227
227
  });
228
228
 
229
229
  it('should register the device and return it', function (done) {
230
- iotAgentLib.retrieveDevice('UNEXISTENT_DEV', '801230BJKL23Y9090DSFL123HJK09H324HV8732', function (
231
- error,
232
- device
233
- ) {
234
- should.not.exist(error);
235
- should.exist(device);
230
+ iotAgentLib.retrieveDevice(
231
+ 'UNEXISTENT_DEV',
232
+ '801230BJKL23Y9090DSFL123HJK09H324HV8732',
233
+ function (error, device) {
234
+ should.not.exist(error);
235
+ should.exist(device);
236
236
 
237
- device.id.should.equal('UNEXISTENT_DEV');
238
- should.exist(device.protocol);
239
- device.protocol.should.equal('MQTT_UL');
240
- done();
241
- });
237
+ device.id.should.equal('UNEXISTENT_DEV');
238
+ should.exist(device.protocol);
239
+ device.protocol.should.equal('MQTT_UL');
240
+ done();
241
+ }
242
+ );
242
243
  });
243
244
  });
244
245
 
245
246
  describe('When an unexisting device tries to be retrieved for an unexisting APIKey', function () {
246
247
  it('should raise an error', function (done) {
247
- iotAgentLib.retrieveDevice('UNEXISTENT_DEV_AND_GROUP', 'H2332Y909DSF3H346yh20JK092', function (
248
- error,
249
- device
250
- ) {
251
- should.exist(error);
252
- error.name.should.equal('DEVICE_GROUP_NOT_FOUND');
253
- should.not.exist(device);
254
- done();
255
- });
248
+ iotAgentLib.retrieveDevice(
249
+ 'UNEXISTENT_DEV_AND_GROUP',
250
+ 'H2332Y909DSF3H346yh20JK092',
251
+ function (error, device) {
252
+ should.exist(error);
253
+ error.name.should.equal('DEVICE_GROUP_NOT_FOUND');
254
+ should.not.exist(device);
255
+ done();
256
+ }
257
+ );
256
258
  });
257
259
  });
258
260
  });
@@ -66,7 +66,7 @@ describe('NGSI-LD - Startup tests', function () {
66
66
  beforeEach(function () {
67
67
  process.env.IOTA_CB_HOST = 'cbhost';
68
68
  process.env.IOTA_CB_PORT = '1111';
69
- process.env.IOTA_CB_NGSI_VERSION = 'v2';
69
+ process.env.IOTA_CB_NGSI_VERSION = 'ld';
70
70
  process.env.IOTA_NORTH_HOST = 'localhost';
71
71
  process.env.IOTA_NORTH_PORT = '2222';
72
72
  process.env.IOTA_PROVIDER_URL = 'provider:3333';
@@ -83,6 +83,11 @@ describe('NGSI-LD - Startup tests', function () {
83
83
  process.env.IOTA_MONGO_DB = 'themongodb';
84
84
  process.env.IOTA_MONGO_REPLICASET = 'customReplica';
85
85
  process.env.IOTA_DEFAULT_RESOURCE = '/iot/custom';
86
+ process.env.IOTA_JSON_LD_CONTEXT = 'http://context.jsonld';
87
+ process.env.IOTA_FALLBACK_TENANT = 'openiot';
88
+ process.env.IOTA_FALLBACK_PATH = 'smartgondor';
89
+ process.env.IOTA_LD_SUPPORT_NULL = 'false';
90
+ process.env.IOTA_LD_SUPPORT_DATASET_ID = 'false';
86
91
 
87
92
  nock.cleanAll();
88
93
 
@@ -114,6 +119,11 @@ describe('NGSI-LD - Startup tests', function () {
114
119
  delete process.env.IOTA_MONGO_DB;
115
120
  delete process.env.IOTA_MONGO_REPLICASET;
116
121
  delete process.env.IOTA_DEFAULT_RESOURCE;
122
+ delete process.env.IOTA_JSON_LD_CONTEXT;
123
+ delete process.env.IOTA_FALLBACK_TENANT;
124
+ delete process.env.IOTA_FALLBACK_PATH;
125
+ delete process.env.IOTA_LD_SUPPORT_NULL;
126
+ delete process.env.IOTA_LD_SUPPORT_DATASET_ID;
117
127
  });
118
128
 
119
129
  afterEach(function (done) {
@@ -123,7 +133,12 @@ describe('NGSI-LD - Startup tests', function () {
123
133
  it('should load the correct configuration parameters', function (done) {
124
134
  iotAgentLib.activate(iotAgentConfig, function (error) {
125
135
  config.getConfig().contextBroker.url.should.equal('http://cbhost:1111');
126
- config.getConfig().contextBroker.ngsiVersion.should.equal('v2');
136
+ config.getConfig().contextBroker.ngsiVersion.should.equal('ld');
137
+ config.getConfig().contextBroker.jsonLdContext.should.equal('http://context.jsonld');
138
+ config.getConfig().contextBroker.fallbackTenant.should.equal( 'openiot');
139
+ config.getConfig().contextBroker.fallbackPath.should.equal('smartgondor');
140
+ config.getConfig().server.ldSupport.null.should.equal(false);
141
+ config.getConfig().server.ldSupport.datasetId.should.equal(false);
127
142
  config.getConfig().server.host.should.equal('localhost');
128
143
  config.getConfig().server.port.should.equal('2222');
129
144
  config.getConfig().providerUrl.should.equal('provider:3333');
@@ -184,7 +184,6 @@ describe('NGSI-LD - Command functionalities', function () {
184
184
  };
185
185
 
186
186
  beforeEach(function (done) {
187
- logger.setLevel('ERROR');
188
187
  iotAgentLib.register(device3, function (error) {
189
188
  done();
190
189
  });
@@ -287,7 +286,6 @@ describe('NGSI-LD - Command functionalities', function () {
287
286
  };
288
287
 
289
288
  beforeEach(function (done) {
290
- logger.setLevel('ERROR');
291
289
  iotAgentLib.register(device3, function (error) {
292
290
  done();
293
291
  });
@@ -368,7 +366,7 @@ describe('NGSI-LD - Command functionalities', function () {
368
366
  describe('When a sequential command with datasetId updates via PATCH /attrs/attr-name arrives to the IoT Agent', function () {
369
367
  const options = {
370
368
  url: 'http://localhost:' + iotAgentConfig.server.port + '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs',
371
- method: 'PUT',
369
+ method: 'PATCH',
372
370
  json: {
373
371
  position: [
374
372
  {
@@ -390,7 +388,6 @@ describe('NGSI-LD - Command functionalities', function () {
390
388
  };
391
389
 
392
390
  beforeEach(function (done) {
393
- logger.setLevel('ERROR');
394
391
  iotAgentLib.register(device3, function (error) {
395
392
  done();
396
393
  });
@@ -496,7 +493,6 @@ describe('NGSI-LD - Command functionalities', function () {
496
493
  };
497
494
 
498
495
  beforeEach(function (done) {
499
- logger.setLevel('ERROR');
500
496
  iotAgentLib.register(device3, function (error) {
501
497
  done();
502
498
  });
@@ -594,7 +590,6 @@ describe('NGSI-LD - Command functionalities', function () {
594
590
  };
595
591
 
596
592
  beforeEach(function (done) {
597
- logger.setLevel('ERROR');
598
593
  iotAgentLib.register(device3, function (error) {
599
594
  done();
600
595
  });
@@ -691,7 +686,6 @@ describe('NGSI-LD - Command functionalities', function () {
691
686
  };
692
687
 
693
688
  beforeEach(function (done) {
694
- logger.setLevel('ERROR');
695
689
  iotAgentLib.register(device3, function (error) {
696
690
  done();
697
691
  });
@@ -794,7 +788,6 @@ describe('NGSI-LD - Command functionalities', function () {
794
788
  };
795
789
 
796
790
  beforeEach(function (done) {
797
- logger.setLevel('ERROR');
798
791
  iotAgentLib.register(device3, function (error) {
799
792
  done();
800
793
  });
@@ -902,7 +895,6 @@ describe('NGSI-LD - Command functionalities', function () {
902
895
  };
903
896
 
904
897
  beforeEach(function (done) {
905
- logger.setLevel('ERROR');
906
898
  iotAgentLib.register(device3, function (error) {
907
899
  done();
908
900
  });
@@ -1006,7 +998,6 @@ describe('NGSI-LD - Command functionalities', function () {
1006
998
  };
1007
999
 
1008
1000
  beforeEach(function (done) {
1009
- logger.setLevel('ERROR');
1010
1001
  iotAgentLib.register(device3, function (error) {
1011
1002
  done();
1012
1003
  });
@@ -1104,7 +1095,6 @@ describe('NGSI-LD - Command functionalities', function () {
1104
1095
  };
1105
1096
 
1106
1097
  beforeEach(function (done) {
1107
- logger.setLevel('ERROR');
1108
1098
  iotAgentLib.register(device3, function (error) {
1109
1099
  done();
1110
1100
  });
@@ -1246,7 +1236,6 @@ describe('NGSI-LD - Command functionalities', function () {
1246
1236
  };
1247
1237
 
1248
1238
  beforeEach(function (done) {
1249
- logger.setLevel('ERROR');
1250
1239
  iotAgentLib.register(device3, function (error) {
1251
1240
  done();
1252
1241
  });
@@ -0,0 +1,249 @@
1
+ /*
2
+ * Copyright 2020 Telefonica Investigación y Desarrollo, S.A.U
3
+ *
4
+ * This file is part of fiware-iotagent-lib
5
+ *
6
+ * fiware-iotagent-lib is free software: you can redistribute it and/or
7
+ * modify it under the terms of the GNU Affero General Public License as
8
+ * published by the Free Software Foundation, either version 3 of the License,
9
+ * or (at your option) any later version.
10
+ *
11
+ * fiware-iotagent-lib is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+ * See the GNU Affero General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Affero General Public
17
+ * License along with fiware-iotagent-lib.
18
+ * If not, seehttp://www.gnu.org/licenses/.
19
+ *
20
+ * For those usages not covered by the GNU Affero General Public License
21
+ * please contact with::[contacto@tid.es]
22
+ *
23
+ * Modified by: Jason Fox - FIWARE Foundation
24
+ */
25
+
26
+ /* eslint-disable no-unused-vars */
27
+
28
+ const iotAgentLib = require('../../../../lib/fiware-iotagent-lib');
29
+ const utils = require('../../../tools/utils');
30
+ const request = utils.request;
31
+ const should = require('should');
32
+ const logger = require('logops');
33
+ const nock = require('nock');
34
+ const mongoUtils = require('../../mongodb/mongoDBUtils');
35
+
36
+ const timekeeper = require('timekeeper');
37
+ let contextBrokerMock;
38
+ let statusAttributeMock;
39
+ const iotAgentConfig = {
40
+ contextBroker: {
41
+ host: '192.168.1.1',
42
+ port: '1026',
43
+ ngsiVersion: 'ld',
44
+ jsonLdContext: 'http://context.json-ld'
45
+ },
46
+ server: {
47
+ port: 4041,
48
+ ldSupport: {
49
+ null: true,
50
+ datasetId: false,
51
+ merge: true
52
+ }
53
+ },
54
+ types: {
55
+ Robot: {
56
+ internalAttributes:[],
57
+ commands:[
58
+ {
59
+ name: 'position',
60
+ object_id: 'pos',
61
+ type: 'Object'
62
+ },
63
+ {
64
+ name: 'orientation',
65
+ type: 'Object'
66
+ }
67
+ ],
68
+ lazy: [
69
+ {
70
+ name: 'batteryLevel',
71
+ type: 'Object'
72
+ }
73
+ ],
74
+ staticAttributes: [],
75
+ active: []
76
+ }
77
+ },
78
+ service: 'smartgondor',
79
+ providerUrl: 'http://smartgondor.com'
80
+ };
81
+ const device3 = {
82
+ id: 'r2d2',
83
+ type: 'Robot',
84
+ service: 'smartgondor'
85
+ };
86
+
87
+ describe('NGSI-LD - Merge-Patch functionalities', function () {
88
+ beforeEach(function (done) {
89
+ const time = new Date(1438760101468); // 2015-08-05T07:35:01.468+00:00
90
+ timekeeper.freeze(time);
91
+ nock.cleanAll();
92
+
93
+ contextBrokerMock = nock('http://192.168.1.1:1026')
94
+ .matchHeader('fiware-service', 'smartgondor')
95
+ .post(
96
+ '/ngsi-ld/v1/csourceRegistrations/',
97
+ utils.readExampleFile(
98
+ './test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommandsAndLazy.json'
99
+ )
100
+ )
101
+ .reply(201, null, { Location: '/ngsi-ld/v1/csourceRegistrations/6319a7f5254b05844116584d' });
102
+
103
+ contextBrokerMock
104
+ .matchHeader('fiware-service', 'smartgondor')
105
+ .post('/ngsi-ld/v1/entityOperations/upsert/')
106
+ .reply(204);
107
+
108
+ iotAgentLib.activate(iotAgentConfig, done);
109
+ });
110
+
111
+ afterEach(function (done) {
112
+ timekeeper.reset();
113
+ delete device3.registrationId;
114
+ iotAgentLib.clearAll(function () {
115
+ iotAgentLib.deactivate(function () {
116
+ mongoUtils.cleanDbs(function () {
117
+ nock.cleanAll();
118
+ iotAgentLib.setDataUpdateHandler();
119
+ iotAgentLib.setCommandHandler();
120
+ done();
121
+ });
122
+ });
123
+ });
124
+ });
125
+
126
+ describe('When a merge-patch PATCH arrives to the IoT Agent as Context Provider', function () {
127
+ const options = {
128
+ url:
129
+ 'http://localhost:' +
130
+ iotAgentConfig.server.port +
131
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2',
132
+ method: 'PATCH',
133
+ json: {
134
+ "position": {
135
+ "type": "Property",
136
+ "value": {
137
+ "moveTo" : [12,34],
138
+ "observedAt": "urn:ngsi-ld:null",
139
+ "precision": {
140
+ "value": 0.95,
141
+ "unitCode": "C62"
142
+ }
143
+ }
144
+ },
145
+ "orientation" : "urn:ngsi-ld:null"
146
+ },
147
+ headers: {
148
+ 'fiware-service': 'smartgondor',
149
+ 'content-type': 'application/ld+json'
150
+ }
151
+ };
152
+
153
+ beforeEach(function (done) {
154
+ iotAgentLib.register(device3, function (error) {
155
+ done();
156
+ });
157
+ });
158
+
159
+ it('should call the client handler once', function (done) {
160
+ let handlerCalled = 0;
161
+
162
+ iotAgentLib.setMergePatchHandler(function (id, type, service, subservice, attributes, callback) {
163
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
164
+ type.should.equal(device3.type);
165
+ attributes[0].name.should.equal('position');
166
+ attributes[1].name.should.equal('orientation');
167
+ should.equal(attributes[1].value, null);
168
+ handlerCalled++;
169
+ callback(null, {
170
+ id,
171
+ type,
172
+ attributes: [
173
+ {
174
+ name: 'position',
175
+ type: 'Array',
176
+ value: '[28, -104, 23]'
177
+ }
178
+ ]
179
+ });
180
+ });
181
+
182
+ request(options, function (error, response, body) {
183
+ should.not.exist(error);
184
+ response.statusCode.should.equal(200);
185
+ handlerCalled.should.equal(1);
186
+ done();
187
+ });
188
+ });
189
+ });
190
+
191
+
192
+ xdescribe('When a partial update PATCH with an NGSI-LD Null arrives to the IoT Agent as Context Provider', function () {
193
+ const options = {
194
+ url:
195
+ 'http://localhost:' +
196
+ iotAgentConfig.server.port +
197
+ '/ngsi-ld/v1/entities/urn:ngsi-ld:Robot:r2d2/attrs/position',
198
+ method: 'PATCH',
199
+ json: {
200
+ "type": "Property",
201
+ "value": "urn:ngsi-ld:null"
202
+ },
203
+ headers: {
204
+ 'fiware-service': 'smartgondor',
205
+ 'content-type': 'application/ld+json'
206
+ }
207
+ };
208
+
209
+ beforeEach(function (done) {
210
+ logger.setLevel('FATAL');
211
+ iotAgentLib.register(device3, function (error) {
212
+ done();
213
+ });
214
+ });
215
+
216
+ it('should call the client handler once', function (done) {
217
+ let handlerCalled = 0;
218
+
219
+ iotAgentLib.setCommandHandler(function (id, type, service, subservice, attributes, callback) {
220
+ id.should.equal('urn:ngsi-ld:' + device3.type + ':' + device3.id);
221
+ type.should.equal(device3.type);
222
+ attributes[0].name.should.equal('position');
223
+ should.equal(attributes[0].value, null);
224
+ handlerCalled++;
225
+ callback(null, {
226
+ id,
227
+ type,
228
+ attributes: [
229
+ {
230
+ name: 'position',
231
+ type: 'Array',
232
+ value: null
233
+ }
234
+ ]
235
+ });
236
+ });
237
+
238
+ request(options, function (error, response, body) {
239
+ console.error(error)
240
+ should.not.exist(error);
241
+
242
+
243
+ response.statusCode.should.equal(204);
244
+ handlerCalled.should.equal(1);
245
+ done();
246
+ });
247
+ });
248
+ });
249
+ });