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.
- package/.nyc_output/33364de2-1199-4ec2-b33c-cae063ef8cc4.json +1 -0
- package/.nyc_output/processinfo/{76bc24ff-5fac-4b5a-997d-de2799342eb0.json → 33364de2-1199-4ec2-b33c-cae063ef8cc4.json} +1 -1
- package/.nyc_output/processinfo/index.json +1 -1
- package/CHANGES_NEXT_RELEASE +0 -1
- package/doc/Contribution.md +3 -3
- package/doc/advanced-topics.md +73 -9
- package/doc/api.md +7 -5
- package/doc/architecture.md +52 -5
- package/doc/expressionLanguage.md +18 -3
- package/doc/operations.md +8 -5
- package/doc/usermanual.md +18 -16
- package/docker/Mosquitto/Dockerfile +1 -1
- package/lib/errors.js +9 -1
- package/lib/model/Group.js +2 -1
- package/lib/model/dbConn.js +4 -0
- package/lib/plugins/bidirectionalData.js +104 -6
- package/lib/plugins/expressionPlugin.js +18 -7
- package/lib/plugins/multiEntity.js +42 -29
- package/lib/plugins/pluginUtils.js +17 -0
- package/lib/request-shim.js +2 -1
- package/lib/services/commands/commandService.js +29 -2
- package/lib/services/common/iotManagerService.js +2 -1
- package/lib/services/devices/deviceService.js +35 -9
- package/lib/services/groups/groupRegistryMongoDB.js +13 -12
- package/lib/services/ngsi/entities-NGSI-LD.js +7 -0
- package/lib/services/ngsi/entities-NGSI-v2.js +70 -11
- package/lib/services/ngsi/ngsiService.js +1 -1
- package/lib/services/northBound/contextServer-NGSI-LD.js +110 -15
- package/lib/services/northBound/contextServer-NGSI-v2.js +8 -3
- package/lib/services/northBound/contextServerUtils.js +9 -9
- package/lib/services/northBound/deviceProvisioningServer.js +2 -1
- package/lib/templates/createDevice.json +1 -2
- package/lib/templates/createDeviceLax.json +2 -3
- package/lib/templates/updateDevice.json +1 -2
- package/package.json +24 -24
- package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +14 -0
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +51 -0
- package/test/unit/mongodb/mongoDBUtils.js +2 -2
- package/test/unit/mongodb/mongodb-group-registry-test.js +25 -1
- package/test/unit/mongodb/mongodb-registry-test.js +51 -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/examples/subscriptionRequests/bidirectionalNotificationWithDatasetId.json +21 -0
- package/test/unit/ngsi-ld/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +17 -0
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +995 -27
- package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +112 -0
- package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +111 -0
- package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +221 -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/updateContextExpressionPlugin36.json +12 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin17.json +27 -0
- package/test/unit/ngsiv2/examples/subscriptionRequests/bidirectionalNotificationWithMetadata.json +19 -0
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +10 -8
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +106 -0
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +151 -0
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +115 -0
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +63 -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
|
@@ -314,13 +314,52 @@ function sendQueryValueNgsi2(entityName, attributes, typeInformation, token, cal
|
|
|
314
314
|
);
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
function extractContextFromPayload(payload) {
|
|
318
|
+
//{b: {value: 3,type: "string"},c: {value: false,type: "string"},d:{value: {g: 45},type: "string"},id: "tamcaysanto",type: "WaterTankMulti"}
|
|
319
|
+
//to
|
|
320
|
+
//{b:3,c:false,d:{g:45}}
|
|
321
|
+
|
|
322
|
+
let ctx = {};
|
|
323
|
+
for (const key in payload) {
|
|
324
|
+
if (key !== 'type' && key !== 'id') {
|
|
325
|
+
ctx[key] = payload[key].value;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return ctx;
|
|
329
|
+
}
|
|
317
330
|
/**
|
|
318
331
|
* Remove id, type and any hidden attrs after processing
|
|
319
332
|
*
|
|
320
333
|
* @param {Object} enities Unprocessed entities
|
|
321
334
|
* @param {Object} typeInformation Configuration information for the device.
|
|
322
335
|
*/
|
|
323
|
-
function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
|
|
336
|
+
function removeHiddenAttrsFromMultiEntity(entities, typeInformation, payload) {
|
|
337
|
+
function filterForEntity(entity, explicitAttrsList, typeInformation) {
|
|
338
|
+
let effectiveList = [];
|
|
339
|
+
for (const attr of explicitAttrsList) {
|
|
340
|
+
if (typeof attr === 'string') {
|
|
341
|
+
effectiveList.push(attr);
|
|
342
|
+
} else if (typeof attr === 'object' && attr.object_id) {
|
|
343
|
+
//find objectId in active attributes
|
|
344
|
+
for (const active of typeInformation.active) {
|
|
345
|
+
if (active.object_id === attr.object_id) {
|
|
346
|
+
if (
|
|
347
|
+
active &&
|
|
348
|
+
active.name &&
|
|
349
|
+
active.entity_name &&
|
|
350
|
+
active.entity_name === entity.id
|
|
351
|
+
//if name is an expression it could fail... BUT IT WORKS!
|
|
352
|
+
) {
|
|
353
|
+
effectiveList.push(active.name);
|
|
354
|
+
}
|
|
355
|
+
continue; //object_id must be unique
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return effectiveList;
|
|
361
|
+
}
|
|
362
|
+
|
|
324
363
|
let explicitAttrsList;
|
|
325
364
|
if (typeInformation.explicitAttrs) {
|
|
326
365
|
explicitAttrsList = [];
|
|
@@ -344,15 +383,26 @@ function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
|
|
|
344
383
|
} else if (typeof typeInformation.explicitAttrs === 'string') {
|
|
345
384
|
entities.forEach((entity) => {
|
|
346
385
|
const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(entity);
|
|
347
|
-
const ctx =
|
|
348
|
-
|
|
349
|
-
|
|
386
|
+
const ctx = {
|
|
387
|
+
...jexlParser.extractContext(attsArray),
|
|
388
|
+
...extractContextFromPayload(payload)
|
|
389
|
+
}; //maybe overlapping between this two objects.
|
|
390
|
+
let res = null;
|
|
391
|
+
try {
|
|
392
|
+
res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctx, typeInformation);
|
|
393
|
+
} catch (e) {
|
|
394
|
+
// nothing to do: exception is already logged at info level
|
|
395
|
+
}
|
|
396
|
+
explicitAttrsList = res ? explicitAttrsList.concat(res) : explicitAttrsList;
|
|
350
397
|
});
|
|
351
398
|
}
|
|
352
399
|
}
|
|
353
400
|
if (explicitAttrsList && explicitAttrsList.length >= 0) {
|
|
354
401
|
entities.forEach((entity) => {
|
|
355
|
-
|
|
402
|
+
//some attrs are object_ids {object_id:oid} and need to be resolved for this entity
|
|
403
|
+
//"explicitAttrs" : "attrA?['attrA',{object_id:'foo'}]:['attrB',{object_id:'bar'}]",
|
|
404
|
+
let efectiveAttrsList = filterForEntity(entity, explicitAttrsList, typeInformation);
|
|
405
|
+
const hidden = _.difference(_.keys(entity), efectiveAttrsList);
|
|
356
406
|
logger.debug(context, 'removeHiddenAttrsFromMultiEntity %s from entity %s', hidden, entity);
|
|
357
407
|
hidden.forEach((attr) => {
|
|
358
408
|
delete entity[attr];
|
|
@@ -361,13 +411,14 @@ function removeHiddenAttrsFromMultiEntity(entities, typeInformation) {
|
|
|
361
411
|
}
|
|
362
412
|
return entities;
|
|
363
413
|
}
|
|
414
|
+
|
|
364
415
|
/**
|
|
365
416
|
* Remove id, type and any hidden attrs after processing
|
|
366
417
|
*
|
|
367
418
|
* @param {Object} result An Unprocessed entity
|
|
368
419
|
* @param {Object} typeInformation Configuration information for the device.
|
|
369
420
|
*/
|
|
370
|
-
function removeHiddenAttrs(result, typeInformation) {
|
|
421
|
+
function removeHiddenAttrs(result, typeInformation, payload) {
|
|
371
422
|
delete result.id;
|
|
372
423
|
delete result.type;
|
|
373
424
|
let explicitAttrsList;
|
|
@@ -388,9 +439,17 @@ function removeHiddenAttrs(result, typeInformation) {
|
|
|
388
439
|
}
|
|
389
440
|
} else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'string') {
|
|
390
441
|
const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(result);
|
|
391
|
-
const ctx =
|
|
392
|
-
|
|
393
|
-
|
|
442
|
+
const ctx = {
|
|
443
|
+
...jexlParser.extractContext(attsArray),
|
|
444
|
+
...extractContextFromPayload(payload)
|
|
445
|
+
}; //maybe overlapping between this two objects. Measures not in active attrs.
|
|
446
|
+
let res = null;
|
|
447
|
+
try {
|
|
448
|
+
res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctx, typeInformation);
|
|
449
|
+
} catch (e) {
|
|
450
|
+
// nothing to do: exception is already logged at info level
|
|
451
|
+
}
|
|
452
|
+
explicitAttrsList = res ? res : [];
|
|
394
453
|
}
|
|
395
454
|
if (explicitAttrsList && explicitAttrsList.length >= 0) {
|
|
396
455
|
const hidden = _.difference(_.keys(result), explicitAttrsList);
|
|
@@ -481,7 +540,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
481
540
|
}
|
|
482
541
|
options.json = {
|
|
483
542
|
actionType: 'append',
|
|
484
|
-
entities: removeHiddenAttrsFromMultiEntity(result, typeInformation)
|
|
543
|
+
entities: removeHiddenAttrsFromMultiEntity(result, typeInformation, payload)
|
|
485
544
|
};
|
|
486
545
|
if (config.getConfig().appendMode === true) {
|
|
487
546
|
options.json.actionType = 'append';
|
|
@@ -489,7 +548,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
489
548
|
options.json.actionType = 'update';
|
|
490
549
|
}
|
|
491
550
|
} else {
|
|
492
|
-
options.json = removeHiddenAttrs(result, typeInformation);
|
|
551
|
+
options.json = removeHiddenAttrs(result, typeInformation, payload);
|
|
493
552
|
logger.debug(context, 'typeInformation: %j', typeInformation);
|
|
494
553
|
if (
|
|
495
554
|
'timestamp' in typeInformation && typeInformation.timestamp !== undefined
|
|
@@ -133,7 +133,7 @@ function executeWithDeviceInformation(operationFunction) {
|
|
|
133
133
|
deviceInformation
|
|
134
134
|
);
|
|
135
135
|
const currentType = type ? type : deviceInformation.type;
|
|
136
|
-
config.getGroupRegistry().
|
|
136
|
+
config.getGroupRegistry().getTypeSilently(currentType, function (error, deviceGroup) {
|
|
137
137
|
let typeInformation;
|
|
138
138
|
const configDeviceInfo = config.getConfig().types[currentType];
|
|
139
139
|
if (error) {
|
|
@@ -42,9 +42,25 @@ const notificationTemplateNgsiLD = require('../../templates/notificationTemplate
|
|
|
42
42
|
const contextServerUtils = require('./contextServerUtils');
|
|
43
43
|
const ngsiLD = require('../ngsi/entities-NGSI-LD');
|
|
44
44
|
|
|
45
|
-
const
|
|
45
|
+
const overwritePaths = ['/ngsi-ld/v1/entities/:entity/attrs', '/ngsi-ld/v1/entities/:entity/attrs/:attr'];
|
|
46
|
+
const updatePaths = ['/ngsi-ld/v1/entities/:entity/attrs', '/ngsi-ld/v1/entities/:entity/attrs/:attr'];
|
|
46
47
|
const queryPaths = ['/ngsi-ld/v1/entities/:entity'];
|
|
47
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Extract metadata attributes from input.
|
|
51
|
+
*
|
|
52
|
+
* @param {Object} obj input object
|
|
53
|
+
*/
|
|
54
|
+
function getMetaData(obj) {
|
|
55
|
+
const excludedKeys = ['datasetId', 'value', 'type'];
|
|
56
|
+
const metaData = {};
|
|
57
|
+
_.keys(obj).forEach((key) => {
|
|
58
|
+
if (!excludedKeys.includes(key)) {
|
|
59
|
+
metaData[key] = obj[key];
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return !_.isEmpty(metaData) ? metaData : {};
|
|
63
|
+
}
|
|
48
64
|
/**
|
|
49
65
|
* Generate all the update actions corresponding to a update context request using Ngsi2.
|
|
50
66
|
* Update actions include updates in attributes and execution of commands.
|
|
@@ -55,8 +71,11 @@ const queryPaths = ['/ngsi-ld/v1/entities/:entity'];
|
|
|
55
71
|
function generateUpdateActionsNgsiLD(req, contextElement, callback) {
|
|
56
72
|
let entityId;
|
|
57
73
|
let entityType;
|
|
74
|
+
|
|
58
75
|
const attribute = req.params.attr;
|
|
59
76
|
const value = req.body.value;
|
|
77
|
+
const datasetId = req.body.datasetId;
|
|
78
|
+
const incomingAttrs = !req.params.attr ? _.keys(req.body) : [];
|
|
60
79
|
|
|
61
80
|
if (contextElement.id && contextElement.type) {
|
|
62
81
|
entityId = contextElement.id;
|
|
@@ -72,12 +91,49 @@ function generateUpdateActionsNgsiLD(req, contextElement, callback) {
|
|
|
72
91
|
|
|
73
92
|
if (device.commands) {
|
|
74
93
|
for (const j in device.commands) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
94
|
+
const name = device.commands[j].name;
|
|
95
|
+
if (attribute === name) {
|
|
96
|
+
if (_.isArray(req.body)) {
|
|
97
|
+
req.body[name].forEach((obj) => {
|
|
98
|
+
commands.push({
|
|
99
|
+
type: device.commands[j].type,
|
|
100
|
+
value: obj.value,
|
|
101
|
+
name: name,
|
|
102
|
+
datasetId: obj.datasetId,
|
|
103
|
+
metadata: getMetaData(obj)
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
} else {
|
|
107
|
+
commands.push({
|
|
108
|
+
type: device.commands[j].type,
|
|
109
|
+
value,
|
|
110
|
+
name,
|
|
111
|
+
datasetId,
|
|
112
|
+
metadata: getMetaData(req.body)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
found = true;
|
|
116
|
+
} else if (incomingAttrs.includes(name)) {
|
|
117
|
+
if (_.isArray(req.body[name])) {
|
|
118
|
+
req.body[name].forEach((obj) => {
|
|
119
|
+
commands.push({
|
|
120
|
+
type: device.commands[j].type,
|
|
121
|
+
value: obj.value,
|
|
122
|
+
name: name,
|
|
123
|
+
datasetId: obj.datasetId,
|
|
124
|
+
metadata: getMetaData(obj)
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
const obj = req.body[name];
|
|
129
|
+
commands.push({
|
|
130
|
+
type: device.commands[j].type,
|
|
131
|
+
value: obj.value,
|
|
132
|
+
name: name,
|
|
133
|
+
datasetId: obj.datasetId,
|
|
134
|
+
metadata: getMetaData(obj)
|
|
135
|
+
});
|
|
136
|
+
}
|
|
81
137
|
found = true;
|
|
82
138
|
}
|
|
83
139
|
}
|
|
@@ -280,7 +336,7 @@ function handleQueryNgsiLD(req, res, next) {
|
|
|
280
336
|
|
|
281
337
|
if (device.staticAttributes) {
|
|
282
338
|
let selectedAttributes = [];
|
|
283
|
-
if (attributes === undefined || attributes.length === 0) {
|
|
339
|
+
if (attributes === null || attributes === undefined || attributes.length === 0) {
|
|
284
340
|
selectedAttributes = device.staticAttributes;
|
|
285
341
|
} else {
|
|
286
342
|
selectedAttributes = device.staticAttributes.filter(inAttributes);
|
|
@@ -306,8 +362,8 @@ function handleQueryNgsiLD(req, res, next) {
|
|
|
306
362
|
const results = device.lazy.map(getName);
|
|
307
363
|
callback(null, results);
|
|
308
364
|
} else {
|
|
309
|
-
logger.debug(context, "Couldn't find any attributes. Handling with
|
|
310
|
-
callback(null,
|
|
365
|
+
logger.debug(context, "Couldn't find any attributes. Handling with empty reference");
|
|
366
|
+
callback(null, []);
|
|
311
367
|
}
|
|
312
368
|
}
|
|
313
369
|
|
|
@@ -462,11 +518,25 @@ function handleNotificationNgsiLD(req, res, next) {
|
|
|
462
518
|
/* eslint-disable-next-line no-prototype-builtins */
|
|
463
519
|
if (dataElement.hasOwnProperty(key)) {
|
|
464
520
|
if (key !== 'id' && key !== 'type') {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
521
|
+
if (_.isArray(dataElement[key])) {
|
|
522
|
+
dataElement[key].forEach((obj) => {
|
|
523
|
+
atts.push({
|
|
524
|
+
type: obj.type,
|
|
525
|
+
value: obj.value,
|
|
526
|
+
name: key,
|
|
527
|
+
datasetId: obj.datasetId,
|
|
528
|
+
metadata: getMetaData(obj)
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
} else {
|
|
532
|
+
atts.push({
|
|
533
|
+
type: dataElement[key].type,
|
|
534
|
+
value: dataElement[key].value,
|
|
535
|
+
name: key,
|
|
536
|
+
datasetId: dataElement[key].datasetId,
|
|
537
|
+
metadata: getMetaData(dataElement[key])
|
|
538
|
+
});
|
|
539
|
+
}
|
|
470
540
|
}
|
|
471
541
|
}
|
|
472
542
|
}
|
|
@@ -553,6 +623,21 @@ function updateErrorHandlingNgsiLD(error, req, res, next) {
|
|
|
553
623
|
});
|
|
554
624
|
}
|
|
555
625
|
|
|
626
|
+
/**
|
|
627
|
+
* Load unsupported NGSI-LD entity routes and return proper NGSI-LD not supported responses
|
|
628
|
+
*
|
|
629
|
+
* @param {Object} router Express request router object.
|
|
630
|
+
*/
|
|
631
|
+
function loadUnsupportedEndpointsNGSILD(router) {
|
|
632
|
+
const unsupportedEndpoint = function (req, res) {
|
|
633
|
+
return res.status(501).send(new errors.MethodNotSupported(req.method, req.path));
|
|
634
|
+
};
|
|
635
|
+
router.get('/ngsi-ld/v1/entities', unsupportedEndpoint);
|
|
636
|
+
router.post('/ngsi-ld/v1/entities', unsupportedEndpoint);
|
|
637
|
+
router.delete('/ngsi-ld/v1/entities/:entity', unsupportedEndpoint);
|
|
638
|
+
router.delete('/ngsi-ld/v1/entities/:entity/attrs/:attr', unsupportedEndpoint);
|
|
639
|
+
}
|
|
640
|
+
|
|
556
641
|
/**
|
|
557
642
|
* Load the routes related to context dispatching (NGSI10 calls).
|
|
558
643
|
*
|
|
@@ -573,6 +658,15 @@ function loadContextRoutesNGSILD(router) {
|
|
|
573
658
|
updateErrorHandlingNgsiLD
|
|
574
659
|
]);
|
|
575
660
|
}
|
|
661
|
+
|
|
662
|
+
for (i = 0; i < overwritePaths.length; i++) {
|
|
663
|
+
router.put(overwritePaths[i], [
|
|
664
|
+
middlewares.ensureType,
|
|
665
|
+
middlewares.validateJson(updateContextTemplateNgsiLD),
|
|
666
|
+
handleUpdateNgsiLD,
|
|
667
|
+
updateErrorHandlingNgsiLD
|
|
668
|
+
]);
|
|
669
|
+
}
|
|
576
670
|
for (i = 0; i < queryPaths.length; i++) {
|
|
577
671
|
router.get(queryPaths[i], [handleQueryNgsiLD, queryErrorHandlingNgsiLD]);
|
|
578
672
|
}
|
|
@@ -582,6 +676,7 @@ function loadContextRoutesNGSILD(router) {
|
|
|
582
676
|
handleNotificationNgsiLD,
|
|
583
677
|
queryErrorHandlingNgsiLD
|
|
584
678
|
]);
|
|
679
|
+
loadUnsupportedEndpointsNGSILD(router);
|
|
585
680
|
}
|
|
586
681
|
|
|
587
682
|
exports.loadContextRoutes = loadContextRoutesNGSILD;
|
|
@@ -217,7 +217,11 @@ function handleUpdateNgsi2(req, res, next) {
|
|
|
217
217
|
}
|
|
218
218
|
);
|
|
219
219
|
} else {
|
|
220
|
-
logger.error(
|
|
220
|
+
logger.error(
|
|
221
|
+
context,
|
|
222
|
+
'Tried to handle an update request [%j] before the update handler was stablished.',
|
|
223
|
+
req.body
|
|
224
|
+
);
|
|
221
225
|
|
|
222
226
|
const errorNotFound = new Error({
|
|
223
227
|
message: 'Update handler not found'
|
|
@@ -282,6 +286,7 @@ function handleNotificationNgsi2(req, res, next) {
|
|
|
282
286
|
const att = {};
|
|
283
287
|
att.type = dataElement[key].type;
|
|
284
288
|
att.value = dataElement[key].value;
|
|
289
|
+
att.metadata = dataElement[key].metadata || {};
|
|
285
290
|
att.name = key;
|
|
286
291
|
atts.push(att);
|
|
287
292
|
}
|
|
@@ -381,7 +386,7 @@ function queryErrorHandlingNgsi2(error, req, res, next) {
|
|
|
381
386
|
function updateErrorHandlingNgsi2(error, req, res, next) {
|
|
382
387
|
let code = 500;
|
|
383
388
|
|
|
384
|
-
logger.debug(context, 'Update NGSIv2 error [%
|
|
389
|
+
logger.debug(context, 'Update NGSIv2 error [%j] handing request: %j', error, req.body);
|
|
385
390
|
|
|
386
391
|
if (error.code && String(error.code).match(/^[2345]\d\d$/)) {
|
|
387
392
|
code = error.code;
|
|
@@ -533,7 +538,7 @@ function handleQueryNgsi2(req, res, next) {
|
|
|
533
538
|
logger.debug(context, 'There was an error handling the query: %s.', error);
|
|
534
539
|
next(error);
|
|
535
540
|
} else {
|
|
536
|
-
logger.debug(context, 'Query from [%s] handled successfully.', req.get('host'));
|
|
541
|
+
logger.debug(context, 'Query from [%s] handled successfully. req %s', req.get('host'), req);
|
|
537
542
|
res.status(200).json(result);
|
|
538
543
|
}
|
|
539
544
|
}
|
|
@@ -41,10 +41,10 @@ const commands = require('../commands/commandService');
|
|
|
41
41
|
* @return {String} The Tenant decribed in the request headers
|
|
42
42
|
*/
|
|
43
43
|
function getLDTenant(req) {
|
|
44
|
-
if (req.
|
|
45
|
-
return req.
|
|
46
|
-
} else if (req.
|
|
47
|
-
return req.
|
|
44
|
+
if (req.header('NGSILD-Tenant')) {
|
|
45
|
+
return req.header('NGSILD-Tenant');
|
|
46
|
+
} else if (req.header('fiware-service')) {
|
|
47
|
+
return req.header('fiware-service');
|
|
48
48
|
}
|
|
49
49
|
return config.getConfig().contextBroker.fallbackTenant;
|
|
50
50
|
}
|
|
@@ -56,10 +56,10 @@ function getLDTenant(req) {
|
|
|
56
56
|
* obliged to offer service headers - this is still being defined in the NGSI-LD specifications.
|
|
57
57
|
*/
|
|
58
58
|
function getLDPath(req) {
|
|
59
|
-
if (req.
|
|
60
|
-
return req.
|
|
61
|
-
} else if (req.
|
|
62
|
-
return req.
|
|
59
|
+
if (req.header('NGSILD-Path')) {
|
|
60
|
+
return req.header('NGSILD-path');
|
|
61
|
+
} else if (req.header('fiware-servicepath')) {
|
|
62
|
+
return req.header('fiware-servicepath');
|
|
63
63
|
}
|
|
64
64
|
return config.getConfig().contextBroker.fallbackPath;
|
|
65
65
|
}
|
|
@@ -112,7 +112,7 @@ function executeUpdateSideEffects(device, id, type, service, subservice, attribu
|
|
|
112
112
|
* @param {Array} attributes List of attributes to update with their types and values.
|
|
113
113
|
*/
|
|
114
114
|
function pushCommandsToQueue(device, id, type, service, subservice, attributes, callback) {
|
|
115
|
-
async.map(attributes, apply(commands.
|
|
115
|
+
async.map(attributes, apply(commands.addCmd, service, subservice, device), callback);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
exports.notificationMiddlewares = [];
|
|
@@ -63,7 +63,8 @@ const provisioningAPITranslation = {
|
|
|
63
63
|
autoprovision: 'autoprovision',
|
|
64
64
|
explicitAttrs: 'explicitAttrs',
|
|
65
65
|
expressionLanguage: 'expressionLanguage',
|
|
66
|
-
ngsiVersion: 'ngsiVersion'
|
|
66
|
+
ngsiVersion: 'ngsiVersion',
|
|
67
|
+
entityNameExp: 'entityNameExp'
|
|
67
68
|
};
|
|
68
69
|
|
|
69
70
|
/**
|
|
@@ -41,8 +41,7 @@
|
|
|
41
41
|
"type": "string"
|
|
42
42
|
},
|
|
43
43
|
"explicitAttrs": {
|
|
44
|
-
"description": "Flag about only provisioned attributes will be processed to Context Broker"
|
|
45
|
-
"type": "boolean"
|
|
44
|
+
"description": "Flag about only provisioned attributes will be processed to Context Broker"
|
|
46
45
|
},
|
|
47
46
|
"ngsiVersion": {
|
|
48
47
|
"description": "NGSI Interface for this device",
|
|
@@ -41,8 +41,7 @@
|
|
|
41
41
|
"type": "boolean"
|
|
42
42
|
},
|
|
43
43
|
"explicitAttrs": {
|
|
44
|
-
"description": "Flag about only provisioned attributes will be processed to Context Broker"
|
|
45
|
-
"type": "boolean"
|
|
44
|
+
"description": "Flag about only provisioned attributes will be processed to Context Broker"
|
|
46
45
|
},
|
|
47
46
|
"ngsiVersion": {
|
|
48
47
|
"description": "NGSI Interface for this device",
|
|
@@ -181,4 +180,4 @@
|
|
|
181
180
|
}
|
|
182
181
|
}
|
|
183
182
|
}
|
|
184
|
-
}
|
|
183
|
+
}
|
|
@@ -198,8 +198,7 @@
|
|
|
198
198
|
"type": "array"
|
|
199
199
|
},
|
|
200
200
|
"explicitAttrs": {
|
|
201
|
-
"description": "Flag to decide update of active attributes only"
|
|
202
|
-
"type": "Boolean"
|
|
201
|
+
"description": "Flag to decide update of active attributes only"
|
|
203
202
|
},
|
|
204
203
|
"ngsiVersion": {
|
|
205
204
|
"description": "NGSI Interface for this device",
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "iotagent-node-lib",
|
|
3
3
|
"license": "AGPL-3.0-only",
|
|
4
4
|
"description": "IoT Agent library to interface with NGSI Context Broker",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.24.0",
|
|
6
6
|
"homepage": "https://github.com/telefonicaid/iotagent-node-lib",
|
|
7
7
|
"keywords": [
|
|
8
8
|
"fiware",
|
|
@@ -43,41 +43,41 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"async": "2.6.4",
|
|
46
|
-
"body-parser": "~1.
|
|
47
|
-
"express": "~4.
|
|
48
|
-
"got": "~11.8.
|
|
46
|
+
"body-parser": "~1.20.0",
|
|
47
|
+
"express": "~4.18.1",
|
|
48
|
+
"got": "~11.8.5",
|
|
49
49
|
"jexl": "2.3.0",
|
|
50
50
|
"jison": "0.4.18",
|
|
51
51
|
"logops": "2.1.2",
|
|
52
52
|
"moment": "~2.29.2",
|
|
53
|
-
"moment-timezone": "~0.5.
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"query-string": "6.5.0",
|
|
53
|
+
"moment-timezone": "~0.5.34",
|
|
54
|
+
"mongoose": "5.13.14",
|
|
55
|
+
"query-string": "7.1.1",
|
|
57
56
|
"revalidator": "~0.3.1",
|
|
58
|
-
"underscore": "~1.
|
|
59
|
-
"uuid": "~
|
|
57
|
+
"underscore": "~1.13.4",
|
|
58
|
+
"uuid": "~8.3.2"
|
|
60
59
|
},
|
|
61
60
|
"devDependencies": {
|
|
62
|
-
"coveralls": "~3.1.
|
|
63
|
-
"eslint": "~
|
|
64
|
-
"eslint-config-tamia": "~
|
|
65
|
-
"eslint-plugin-prettier": "~
|
|
61
|
+
"coveralls": "~3.1.1",
|
|
62
|
+
"eslint": "~8.18.0",
|
|
63
|
+
"eslint-config-tamia": "~8.0.0",
|
|
64
|
+
"eslint-plugin-prettier": "~4.0.0",
|
|
66
65
|
"husky": "~4.2.5",
|
|
67
|
-
"lint-staged": "~
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"nock": "13.
|
|
66
|
+
"lint-staged": "~12.3.8",
|
|
67
|
+
"mocha": "10.0.0",
|
|
68
|
+
"mongodb": "4.7.0",
|
|
69
|
+
"nock": "13.2.7",
|
|
71
70
|
"nyc": "~15.1.0",
|
|
72
|
-
"
|
|
73
|
-
"remark-
|
|
71
|
+
"prettier": "~2.7.1",
|
|
72
|
+
"remark-cli": "~10.0.1",
|
|
73
|
+
"remark-preset-lint-recommended": "~6.1.2",
|
|
74
74
|
"should": "13.2.3",
|
|
75
|
-
"sinon": "~
|
|
76
|
-
"textlint": "~
|
|
75
|
+
"sinon": "~14.0.0",
|
|
76
|
+
"textlint": "~12.2.1",
|
|
77
77
|
"textlint-filter-rule-comments": "~1.2.2",
|
|
78
78
|
"textlint-rule-common-misspellings": "~1.0.1",
|
|
79
|
-
"textlint-rule-terminology": "~
|
|
80
|
-
"textlint-rule-write-good": "~
|
|
79
|
+
"textlint-rule-terminology": "~3.0.2",
|
|
80
|
+
"textlint-rule-write-good": "~2.0.0",
|
|
81
81
|
"timekeeper": "2.2.0",
|
|
82
82
|
"watch": "~1.0.2"
|
|
83
83
|
},
|
|
@@ -194,4 +194,55 @@ describe('NGSI-v2 - In memory device registry', function () {
|
|
|
194
194
|
});
|
|
195
195
|
});
|
|
196
196
|
});
|
|
197
|
+
|
|
198
|
+
describe('When a the registry is queried for device in a particular name and type', function (){
|
|
199
|
+
beforeEach(function (done) {
|
|
200
|
+
contextBrokerMock = nock('http://192.168.1.1:1026')
|
|
201
|
+
.post('/v2/entities?options=upsert')
|
|
202
|
+
.times(10)
|
|
203
|
+
.reply(204);
|
|
204
|
+
|
|
205
|
+
const devices = [];
|
|
206
|
+
|
|
207
|
+
for (let i = 0; i < 10; i++)
|
|
208
|
+
{
|
|
209
|
+
devices.push(
|
|
210
|
+
{
|
|
211
|
+
id: 'id' + i,
|
|
212
|
+
name : 'name' + i,
|
|
213
|
+
type: 'Light' + i,
|
|
214
|
+
internalId: 'internal' + i,
|
|
215
|
+
service: 'smartgondor',
|
|
216
|
+
subservice: 'gardens',
|
|
217
|
+
active: [ {
|
|
218
|
+
id: 'attrId',
|
|
219
|
+
type: 'attrType' + i,
|
|
220
|
+
value: i
|
|
221
|
+
}]
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async.map(devices, iotAgentLib.register, function (error, results) {
|
|
226
|
+
done();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
afterEach(function (done) {
|
|
231
|
+
iotAgentLib.clearRegistry(done);
|
|
232
|
+
});
|
|
233
|
+
it('should return the name and type of device', function (done)
|
|
234
|
+
{
|
|
235
|
+
iotAgentLib.getDeviceByNameAndType('name5', 'Light5','smartgondor', 'gardens' ,function(error, device)
|
|
236
|
+
{
|
|
237
|
+
should.not.exist(error);
|
|
238
|
+
should.exist(device);
|
|
239
|
+
should.exist(device.name);
|
|
240
|
+
should.exist(device.type);
|
|
241
|
+
device.name.should.equal('name5');
|
|
242
|
+
device.type.should.equal('Light5');
|
|
243
|
+
Object.keys(device).length.should.equal(11);
|
|
244
|
+
done();
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
});
|
|
197
248
|
});
|
|
@@ -29,7 +29,7 @@ const async = require('async');
|
|
|
29
29
|
function cleanDb(host, name, callback) {
|
|
30
30
|
const url = 'mongodb://' + host + ':27017/' + name;
|
|
31
31
|
|
|
32
|
-
MongoClient.connect(url,
|
|
32
|
+
MongoClient.connect(url, function (err, db) {
|
|
33
33
|
if (db && db.db()) {
|
|
34
34
|
db.db().dropDatabase(function (err, result) {
|
|
35
35
|
db.close();
|
|
@@ -46,7 +46,7 @@ function cleanDbs(callback) {
|
|
|
46
46
|
function populate(host, dbName, entityList, collectionName, callback) {
|
|
47
47
|
const url = 'mongodb://' + host + ':27017/' + dbName;
|
|
48
48
|
|
|
49
|
-
MongoClient.connect(url,
|
|
49
|
+
MongoClient.connect(url, function (err, db) {
|
|
50
50
|
if (db) {
|
|
51
51
|
db.db()
|
|
52
52
|
.collection(collectionName)
|