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.
- package/.nyc_output/6e3d7795-bf8c-4a50-bd2f-f8221f27aeae.json +1 -0
- package/.nyc_output/processinfo/6e3d7795-bf8c-4a50-bd2f-f8221f27aeae.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -1
- package/.readthedocs.yml +3 -1
- package/CHANGES_NEXT_RELEASE +1 -0
- package/README.md +2 -2
- package/config +0 -0
- package/doc/Contribution.md +3 -3
- package/doc/advanced-topics.md +19 -8
- package/doc/api.md +14 -3
- package/doc/expressionLanguage.md +17 -0
- package/doc/northboundinteractions.md +40 -33
- package/doc/operations.md +8 -5
- package/doc/requirements.txt +4 -0
- package/{docs → doc}/roadmap.md +21 -6
- package/doc/usermanual.md +4 -4
- package/docker/Mosquitto/Dockerfile +28 -12
- package/docker/Mosquitto/Dockerfile.debian +38 -0
- package/docker/Mosquitto/Dockerfile.debian~ +23 -0
- package/docker/Mosquitto/README.md +8 -7
- package/docker/Mosquitto/startMosquitto.sh +8 -0
- package/lib/fiware-iotagent-lib.js +1 -0
- package/lib/jexlTranformsMap.js +3 -1
- package/lib/model/Group.js +2 -1
- package/lib/model/dbConn.js +4 -0
- package/lib/plugins/expressionPlugin.js +56 -21
- package/lib/plugins/multiEntity.js +43 -49
- package/lib/plugins/multiEntity.js_avg2 +343 -0
- package/lib/plugins/pluginUtils.js +16 -0
- package/lib/services/commands/commandService.js +29 -2
- package/lib/services/common/iotManagerService.js +2 -1
- package/lib/services/devices/deviceRegistryMemory.js +13 -2
- package/lib/services/devices/deviceRegistryMongoDB.js +15 -7
- package/lib/services/devices/deviceService.js +52 -16
- package/lib/services/groups/groupRegistryMongoDB.js +13 -12
- package/lib/services/ngsi/entities-NGSI-LD.js +13 -4
- package/lib/services/ngsi/entities-NGSI-v2.js +64 -13
- package/lib/services/ngsi/ngsiService.js +3 -3
- package/lib/services/northBound/contextServer-NGSI-LD.js +20 -1
- package/lib/services/northBound/contextServer-NGSI-v2.js +39 -30
- package/lib/services/northBound/contextServerUtils.js +10 -10
- package/lib/services/northBound/deviceProvisioningServer.js +4 -1
- package/lib/templates/createDevice.json +13 -2
- package/lib/templates/createDeviceLax.json +2 -3
- package/lib/templates/updateDevice.json +13 -2
- package/package.json +26 -26
- package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +14 -0
- package/test/unit/mongodb/mongoDBUtils.js +2 -2
- package/test/unit/mongodb/mongodb-group-registry-test.js +1 -1
- package/test/unit/mongodb/mongodb-registry-test.js +2 -3
- package/test/unit/ngsi-ld/examples/contextAvailabilityRequests/registerIoTAgentCommands.json +2 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextLanguageProperties1.json +15 -0
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +315 -1
- package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +112 -0
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +1 -1
- package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +8 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextCommandStatus2.json +6 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +2 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin36.json +12 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +27 -0
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +72 -9
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +151 -0
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +63 -0
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js_avg2 +1224 -0
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +60 -0
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +2 -1
- package/.nyc_output/76bc24ff-5fac-4b5a-997d-de2799342eb0.json +0 -1
- 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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
317
|
+
object_id: 'theLocation',
|
|
318
|
+
name: 'mylocation',
|
|
319
|
+
type: 'geo:json'
|
|
320
320
|
}
|
|
321
321
|
],
|
|
322
|
-
explicitAttrs:
|
|
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: '
|
|
1164
|
-
type: '
|
|
1165
|
-
value: '
|
|
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/
|
|
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
|
{
|