iotagent-node-lib 3.0.0 → 3.2.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/.github/workflows/ci.yml +1 -0
- package/config.js +5 -5
- package/doc/api.md +1540 -298
- package/doc/deprecated.md +3 -1
- package/doc/development.md +120 -0
- package/doc/installationguide.md +3 -6
- package/docker-compose-dev.yml +1 -1
- package/lib/commonConfig.js +7 -10
- package/lib/fiware-iotagent-lib.js +0 -10
- package/lib/jexlTranformsMap.js +2 -1
- package/lib/plugins/bidirectionalData.js +8 -26
- package/lib/plugins/expressionPlugin.js +8 -40
- package/lib/plugins/jexlParser.js +28 -0
- package/lib/services/commands/commandService.js +1 -1
- package/lib/services/devices/deviceService.js +2 -1
- package/lib/services/ngsi/entities-NGSI-LD.js +15 -73
- package/lib/services/ngsi/entities-NGSI-v2.js +149 -124
- package/lib/services/northBound/deviceProvisioningServer.js +17 -14
- package/lib/templates/createDevice.json +5 -2
- package/lib/templates/createDeviceLax.json +7 -5
- package/lib/templates/updateDevice.json +5 -2
- package/lib/templates/updateDeviceLax.json +3 -5
- package/package.json +2 -2
- package/test/unit/examples/deviceProvisioningRequests/provisionBidirectionalDevice.json +5 -5
- package/test/unit/examples/deviceProvisioningRequests/provisionMinimumDevice4.json +1 -0
- package/test/unit/examples/groupProvisioningRequests/bidirectionalGroup.json +4 -4
- package/test/unit/general/config-multi-core-test.js +2 -1
- package/test/unit/general/contextBrokerKeystoneSecurityAccess-test.js +2 -1
- package/test/unit/general/deviceService-test.js +2 -1
- package/test/unit/general/loglevel-api_test.js +2 -1
- package/test/unit/general/startup-test.js +2 -1
- package/test/unit/general/statistics-persistence_test.js +1 -0
- package/test/unit/general/statistics-service_test.js +1 -0
- package/test/unit/lazyAndCommands/commandRegistry_test.js +1 -0
- package/test/unit/memoryRegistry/deviceRegistryMemory_test.js +1 -0
- package/test/unit/mongodb/mongodb-connectionoptions-test.js +1 -0
- package/test/unit/mongodb/mongodb-group-registry-test.js +1 -0
- package/test/unit/mongodb/mongodb-registry-test.js +2 -1
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +0 -2
- package/test/unit/ngsi-ld/general/config-jsonld-contexts-test.js +2 -1
- package/test/unit/ngsi-ld/general/contextBrokerOAuthSecurityAccess-test.js +2 -1
- package/test/unit/ngsi-ld/general/deviceService-test.js +2 -1
- package/test/unit/ngsi-ld/general/https-support-test.js +2 -1
- package/test/unit/ngsi-ld/general/iotam-autoregistration-test.js +2 -1
- package/test/unit/ngsi-ld/general/startup-test.js +3 -2
- package/test/unit/ngsi-ld/lazyAndCommands/active-devices-attribute-update-test.js +2 -1
- package/test/unit/ngsi-ld/lazyAndCommands/command-test.js +5 -7
- package/test/unit/ngsi-ld/lazyAndCommands/lazy-devices-test.js +8 -6
- package/test/unit/ngsi-ld/lazyAndCommands/merge-patch-test.js +18 -22
- package/test/unit/ngsi-ld/lazyAndCommands/polling-commands-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/active-devices-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/autocast-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/geoproperties-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/languageProperties-test.js +32 -34
- package/test/unit/ngsi-ld/ngsiService/staticAttributes-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/subscriptions-test.js +2 -1
- package/test/unit/ngsi-ld/ngsiService/unsupported-endpoints-test.js +11 -15
- package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +2 -1
- package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +10 -9
- package/test/unit/ngsi-ld/plugins/compress-timestamp-plugin_test.js +2 -1
- package/test/unit/ngsi-ld/plugins/custom-plugin_test.js +152 -0
- package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +3 -2
- package/test/unit/ngsi-ld/plugins/timestamp-processing-plugin_test.js +2 -1
- package/test/unit/ngsi-ld/provisioning/device-provisioning-api_test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/device-registration_test.js +2 -1
- package/test/unit/ngsi-ld/provisioning/device-update-registration_test.js +15 -12
- package/test/unit/ngsi-ld/provisioning/listProvisionedDevices-test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/provisionDeviceMultientity-test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/removeProvisionedDevice-test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/singleConfigurationMode-test.js +1 -0
- package/test/unit/ngsi-ld/provisioning/updateProvisionedDevices-test.js +1 -0
- package/test/unit/ngsi-mixed/provisioning/ngsi-versioning-test.js +13 -7
- package/test/unit/ngsiv2/examples/contextRequests/createMinimumProvisionedDevice4.json +5 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin31.json +0 -8
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin32.json +6 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin35.json +20 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin40.json +42 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextExpressionPlugin41.json +32 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin25.json +37 -0
- package/test/unit/ngsiv2/examples/contextRequests/updateContextProcessTimestamp.json +7 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverride.json +7 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +7 -1
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +232 -8
- package/test/unit/ngsiv2/general/contextBrokerOAuthSecurityAccess-test.js +2 -1
- package/test/unit/ngsiv2/general/deviceService-test.js +2 -1
- package/test/unit/ngsiv2/general/https-support-test.js +2 -1
- package/test/unit/ngsiv2/general/iotam-autoregistration-test.js +2 -1
- package/test/unit/ngsiv2/general/startup-test.js +2 -1
- package/test/unit/ngsiv2/lazyAndCommands/active-devices-attribute-update-test.js +2 -1
- package/test/unit/ngsiv2/lazyAndCommands/command-test.js +2 -1
- package/test/unit/ngsiv2/lazyAndCommands/lazy-devices-test.js +8 -6
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +3 -3
- package/test/unit/ngsiv2/ngsiService/active-devices-test.js +3 -2
- package/test/unit/ngsiv2/ngsiService/autocast-test.js +2 -1
- package/test/unit/ngsiv2/ngsiService/queryDeviceInformationInCb-test.js +2 -1
- package/test/unit/ngsiv2/ngsiService/staticAttributes-test.js +2 -1
- package/test/unit/ngsiv2/ngsiService/subscriptions-test.js +2 -1
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +2 -1
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +8 -7
- package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +2 -1
- package/test/unit/ngsiv2/plugins/custom-plugin_test.js +151 -0
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +87 -13
- package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +2 -1
- package/test/unit/ngsiv2/provisioning/device-group-api-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/device-group-utils-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +12 -3
- package/test/unit/ngsiv2/provisioning/device-registration_test.js +2 -1
- package/test/unit/ngsiv2/provisioning/device-update-registration_test.js +15 -12
- package/test/unit/ngsiv2/provisioning/listProvisionedDevices-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/provisionDeviceMultientity-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/removeProvisionedDevice-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/singleConfigurationMode-test.js +1 -0
- package/test/unit/ngsiv2/provisioning/updateProvisionedDevices-test.js +1 -0
- package/test/unit/plugins/capture-configuration-inPlugins_test.js +2 -1
- package/test/unit/plugins/capture-provision-inPlugins_test.js +2 -1
- package/doc/advanced-topics.md +0 -626
- package/doc/expressionLanguage.md +0 -762
- package/lib/plugins/expressionParser.js +0 -205
- package/test/unit/expressions/expression-test.js +0 -197
- package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +0 -881
- package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -950
- package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -294
|
@@ -103,7 +103,7 @@ function formatGeoAttrs(attr) {
|
|
|
103
103
|
* @param Boolean skipMetadataAtt An optional flag to indicate if timestamp should be added to each metadata attribute. Default is false
|
|
104
104
|
* @return {Object} NGSIv2 payload entities with timestamp
|
|
105
105
|
*/
|
|
106
|
-
function addTimestampNgsi2(payload, timezone, timestampValue
|
|
106
|
+
function addTimestampNgsi2(payload, timezone, timestampValue) {
|
|
107
107
|
function addTimestampEntity(entity, timezone, timestampValue) {
|
|
108
108
|
const timestamp = {
|
|
109
109
|
type: constants.TIMESTAMP_TYPE_NGSI2
|
|
@@ -111,12 +111,10 @@ function addTimestampNgsi2(payload, timezone, timestampValue, skipMetadataAtt) {
|
|
|
111
111
|
|
|
112
112
|
if (timestampValue) {
|
|
113
113
|
timestamp.value = timestampValue;
|
|
114
|
+
} else if (!timezone) {
|
|
115
|
+
timestamp.value = new Date().toISOString();
|
|
114
116
|
} else {
|
|
115
|
-
|
|
116
|
-
timestamp.value = new Date().toISOString();
|
|
117
|
-
} else {
|
|
118
|
-
timestamp.value = moment().tz(timezone).format('YYYY-MM-DD[T]HH:mm:ss.SSSZ');
|
|
119
|
-
}
|
|
117
|
+
timestamp.value = moment().tz(timezone).format('YYYY-MM-DD[T]HH:mm:ss.SSSZ');
|
|
120
118
|
}
|
|
121
119
|
|
|
122
120
|
function addMetadata(attribute) {
|
|
@@ -148,9 +146,7 @@ function addTimestampNgsi2(payload, timezone, timestampValue, skipMetadataAtt) {
|
|
|
148
146
|
for (const key in entity) {
|
|
149
147
|
/* eslint-disable-next-line no-prototype-builtins */
|
|
150
148
|
if (entity.hasOwnProperty(key) && key !== 'id' && key !== 'type') {
|
|
151
|
-
|
|
152
|
-
addMetadata(entity[key]);
|
|
153
|
-
}
|
|
149
|
+
addMetadata(entity[key]);
|
|
154
150
|
keyCount += 1;
|
|
155
151
|
}
|
|
156
152
|
}
|
|
@@ -327,16 +323,6 @@ function sendQueryValueNgsi2(entityName, attributes, typeInformation, token, cal
|
|
|
327
323
|
);
|
|
328
324
|
}
|
|
329
325
|
|
|
330
|
-
/**
|
|
331
|
-
* Determines if a value is of type float
|
|
332
|
-
*
|
|
333
|
-
* @param {String} value Value to be analyzed
|
|
334
|
-
* @return {boolean} True if float, False otherwise.
|
|
335
|
-
*/
|
|
336
|
-
function isFloat(value) {
|
|
337
|
-
return !isNaN(value) && value.toString().indexOf('.') !== -1;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
326
|
/**
|
|
341
327
|
* Makes an update in the Device's entity in the context broker, with the values given in the 'attributes' array. This
|
|
342
328
|
* array should comply to the NGSIv2's attribute format.
|
|
@@ -349,12 +335,12 @@ function isFloat(value) {
|
|
|
349
335
|
function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, callback) {
|
|
350
336
|
logger.debug(
|
|
351
337
|
context,
|
|
352
|
-
'sendUpdateValueNgsi2 called with:
|
|
338
|
+
'sendUpdateValueNgsi2 called with: entityName=%s attributes=%j typeInformation=%j',
|
|
353
339
|
entityName,
|
|
354
340
|
attributes,
|
|
355
341
|
typeInformation
|
|
356
342
|
);
|
|
357
|
-
|
|
343
|
+
const payload = {
|
|
358
344
|
entities: [
|
|
359
345
|
{
|
|
360
346
|
id: entityName
|
|
@@ -384,8 +370,9 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
384
370
|
callback(new errors.TypeNotFound(null, entityName));
|
|
385
371
|
return;
|
|
386
372
|
}
|
|
373
|
+
|
|
387
374
|
let idTypeSSSList = pluginUtils.getIdTypeServSubServiceFromDevice(typeInformation);
|
|
388
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
375
|
+
logger.debug(context, 'sendUpdateValueNgsi2 idTypeSSS are %j ', idTypeSSSList);
|
|
389
376
|
let measureAttrsForCtxt = [];
|
|
390
377
|
|
|
391
378
|
// Check explicitAttrs: adds all final needed attributes to payload
|
|
@@ -410,7 +397,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
410
397
|
return;
|
|
411
398
|
}
|
|
412
399
|
}
|
|
413
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
400
|
+
logger.debug(context, 'sendUpdateValueNgsi2 pre-initial non-explicitAttrs payload=%j', payload);
|
|
414
401
|
// Loop for add attrs from type.information.active (and lazys?) into payload entities (entity[0])
|
|
415
402
|
if (typeInformation.active) {
|
|
416
403
|
typeInformation.active.forEach((attr) => {
|
|
@@ -437,7 +424,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
437
424
|
if (typeof typeInformation.explicitAttrs === 'string') {
|
|
438
425
|
// explicitAttrs is a jexlExpression
|
|
439
426
|
// This ctxt should include all possible attrs
|
|
440
|
-
|
|
427
|
+
const attributesCtxt = [];
|
|
441
428
|
if (typeInformation.static) {
|
|
442
429
|
typeInformation.static.forEach(function (att) {
|
|
443
430
|
attributesCtxt.push(att);
|
|
@@ -446,37 +433,57 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
446
433
|
// Measures
|
|
447
434
|
for (let i = 0; i < attributes.length; i++) {
|
|
448
435
|
if (attributes[i].name && attributes[i].type) {
|
|
449
|
-
|
|
436
|
+
const measureAttr = {
|
|
450
437
|
name: attributes[i].name,
|
|
451
438
|
value: attributes[i].value,
|
|
452
439
|
type: attributes[i].type
|
|
453
440
|
};
|
|
454
441
|
attributesCtxt.push(measureAttr);
|
|
442
|
+
// check measureAttr by object_id -> if in active
|
|
443
|
+
let j = 0;
|
|
444
|
+
let found = false;
|
|
445
|
+
while (j < typeInformation.active.length && !found) {
|
|
446
|
+
if (attributes[i].name === typeInformation.active[j].object_id) {
|
|
447
|
+
let measureAttrByObjectId = {
|
|
448
|
+
name: typeInformation.active[j].name,
|
|
449
|
+
value: attributes[i].value,
|
|
450
|
+
type: attributes[i].type
|
|
451
|
+
};
|
|
452
|
+
attributesCtxt.push(measureAttrByObjectId);
|
|
453
|
+
found = true;
|
|
454
|
+
}
|
|
455
|
+
j++;
|
|
456
|
+
}
|
|
455
457
|
}
|
|
456
458
|
}
|
|
457
459
|
// This context is just to calculate explicitAttrs when is an expression
|
|
458
|
-
|
|
459
|
-
|
|
460
|
+
|
|
461
|
+
let ctxt = expressionPlugin.extractContext(attributesCtxt.concat(idTypeSSSList));
|
|
462
|
+
// typeInformation.active all attrs with expressions
|
|
460
463
|
if (typeInformation.active) {
|
|
461
464
|
typeInformation.active.forEach(function (att) {
|
|
462
|
-
if (att.expression) {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
465
|
+
if (att.expression !== undefined) {
|
|
466
|
+
let expandedAttr = {
|
|
467
|
+
name: att.name,
|
|
468
|
+
value: att.expression,
|
|
469
|
+
type: att.type
|
|
470
|
+
};
|
|
471
|
+
attributesCtxt.push(expandedAttr);
|
|
472
|
+
if (att.object_id !== undefined) {
|
|
473
|
+
let expandedAttrByObjectId = {
|
|
474
|
+
name: att.object_id,
|
|
475
|
+
value: att.expression,
|
|
467
476
|
type: att.type
|
|
468
477
|
};
|
|
469
|
-
attributesCtxt.push(
|
|
470
|
-
ctxt = expressionPlugin.extractContext(
|
|
471
|
-
attributesCtxt.concat(idTypeSSSList),
|
|
472
|
-
typeInformation
|
|
473
|
-
);
|
|
478
|
+
attributesCtxt.push(expandedAttrByObjectId);
|
|
474
479
|
}
|
|
480
|
+
ctxt = expressionPlugin.extractContext(attributesCtxt.concat(idTypeSSSList));
|
|
475
481
|
}
|
|
476
482
|
});
|
|
477
483
|
}
|
|
478
484
|
// calculate expression for explicitAttrs
|
|
479
485
|
try {
|
|
486
|
+
logger.debug(context, 'sendUpdateValueNgsi2 selectedAttrs ctxt %j', ctxt);
|
|
480
487
|
let res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctxt, typeInformation);
|
|
481
488
|
if (res === true) {
|
|
482
489
|
// like explicitAttrs == true
|
|
@@ -502,7 +509,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
502
509
|
// implies do nothing
|
|
503
510
|
logger.info(
|
|
504
511
|
context,
|
|
505
|
-
'sendUpdateValueNgsi2
|
|
512
|
+
'sendUpdateValueNgsi2 none selectedAttrs with %j and ctxt %j',
|
|
506
513
|
typeInformation.explicitAttrs,
|
|
507
514
|
ctxt
|
|
508
515
|
);
|
|
@@ -516,19 +523,42 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
516
523
|
if (selectedAttrs.includes(attr.name)) {
|
|
517
524
|
selectedAttrs.push(attr.object_id);
|
|
518
525
|
}
|
|
526
|
+
// Check if selectedAttrs includes an attribute with format {object_id: xxxx}
|
|
527
|
+
if (selectedAttrs.includes({ object_id: attr.object_id })) {
|
|
528
|
+
selectedAttrs.push(attr.object_id);
|
|
529
|
+
}
|
|
519
530
|
});
|
|
520
531
|
} else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
|
|
521
|
-
//
|
|
522
|
-
//
|
|
532
|
+
// explicitAtts is true => Add just measures which are defined in active attributes
|
|
533
|
+
// and active attributes with expressions
|
|
534
|
+
// and TimeInstant
|
|
535
|
+
selectedAttrs = ['TimeInstant'];
|
|
523
536
|
typeInformation.active.forEach((attr) => {
|
|
524
|
-
|
|
525
|
-
|
|
537
|
+
// Measures
|
|
538
|
+
if (attr.expression !== undefined) {
|
|
539
|
+
selectedAttrs.push(attr.name);
|
|
540
|
+
selectedAttrs.push(attr.object_id);
|
|
541
|
+
} else {
|
|
542
|
+
// check if active attr is receiving a measure
|
|
543
|
+
let i = 0;
|
|
544
|
+
let found = false;
|
|
545
|
+
while (i < attributes.length && !found) {
|
|
546
|
+
if (attributes[i].name && attributes[i].type) {
|
|
547
|
+
if (attributes[i].name === attr.object_id || attributes[i].name === attr.name) {
|
|
548
|
+
selectedAttrs.push(attr.name);
|
|
549
|
+
selectedAttrs.push(attr.object_id);
|
|
550
|
+
found = true;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
i++;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
526
556
|
});
|
|
527
557
|
}
|
|
528
558
|
// This loop adds selected measured values (attributes) into payload entities (entity[0])
|
|
529
559
|
for (let i = 0; i < attributes.length; i++) {
|
|
530
560
|
if (attributes[i].name && selectedAttrs.includes(attributes[i].name) && attributes[i].type) {
|
|
531
|
-
|
|
561
|
+
const attr = typeInformation.active.find((obj) => {
|
|
532
562
|
return obj.name === attributes[i].name;
|
|
533
563
|
});
|
|
534
564
|
payload.entities[0][attributes[i].name] = {
|
|
@@ -544,7 +574,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
544
574
|
payload.entities[0][attributes[i].name].metadata = metadata;
|
|
545
575
|
}
|
|
546
576
|
} else if (attributes[i].name && !selectedAttrs.includes(attributes[i].name) && attributes[i].type) {
|
|
547
|
-
|
|
577
|
+
const att = {
|
|
548
578
|
name: attributes[i].name,
|
|
549
579
|
type: attributes[i].type,
|
|
550
580
|
value: attributes[i].value
|
|
@@ -554,10 +584,15 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
554
584
|
}
|
|
555
585
|
logger.debug(
|
|
556
586
|
context,
|
|
557
|
-
'sendUpdateValueNgsi2
|
|
587
|
+
'sendUpdateValueNgsi2 pre-initial explicitAttrs payload=%j selectedAttrs=%j',
|
|
558
588
|
payload,
|
|
559
589
|
selectedAttrs
|
|
560
590
|
);
|
|
591
|
+
let selectedAttrsByObjectId = selectedAttrs
|
|
592
|
+
.filter((o) => o !== undefined && o.object_id)
|
|
593
|
+
.map(function (el) {
|
|
594
|
+
return el.object_id;
|
|
595
|
+
});
|
|
561
596
|
|
|
562
597
|
// Loop for add seleted attrs from type.information.active into pyaload entities (entity[0])
|
|
563
598
|
if (typeInformation.active) {
|
|
@@ -579,13 +614,23 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
579
614
|
type: attr.type
|
|
580
615
|
};
|
|
581
616
|
}
|
|
617
|
+
} else if (attr.object_id !== undefined && selectedAttrsByObjectId.includes(attr.object_id)) {
|
|
618
|
+
payload.entities[0][attr.object_id] = {
|
|
619
|
+
value: payload.entities[0][attr.object_id]
|
|
620
|
+
? payload.entities[0][attr.object_id].value
|
|
621
|
+
: payload.entities[0][attr.name]
|
|
622
|
+
? payload.entities[0][attr.name].value
|
|
623
|
+
: undefined,
|
|
624
|
+
type: attr.type,
|
|
625
|
+
object_id: attr.object_id
|
|
626
|
+
};
|
|
582
627
|
}
|
|
583
628
|
});
|
|
584
629
|
}
|
|
585
630
|
} // END check explicitAttrs
|
|
586
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
631
|
+
logger.debug(context, 'sendUpdateValueNgsi2 initial payload=%j', payload);
|
|
587
632
|
|
|
588
|
-
|
|
633
|
+
const currentEntity = payload.entities[0];
|
|
589
634
|
|
|
590
635
|
// Prepare attributes for expresionPlugin
|
|
591
636
|
const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(currentEntity);
|
|
@@ -610,11 +655,11 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
610
655
|
}
|
|
611
656
|
attributesCtxt = attributesCtxt.concat(idTypeSSSList);
|
|
612
657
|
let ctxt = expressionPlugin.extractContext(attributesCtxt, typeInformation);
|
|
613
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
658
|
+
logger.debug(context, 'sendUpdateValueNgsi2 initial ctxt %j ', ctxt);
|
|
614
659
|
|
|
615
660
|
// Sort currentEntity to get first attrs without expressions (checking attrs in typeInformation.active)
|
|
616
661
|
// attributes without expressions should be processed before
|
|
617
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
662
|
+
logger.debug(context, 'sendUpdateValueNgsi2 currentEntity %j ', currentEntity);
|
|
618
663
|
if (typeInformation.active && typeInformation.active.length > 0) {
|
|
619
664
|
for (const k in currentEntity) {
|
|
620
665
|
typeInformation.active.forEach(function (att) {
|
|
@@ -622,14 +667,15 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
622
667
|
(att.object_id && att.object_id === k && att.expression) ||
|
|
623
668
|
(att.name && att.name === k && att.expression)
|
|
624
669
|
) {
|
|
625
|
-
|
|
670
|
+
const m = currentEntity[k];
|
|
626
671
|
delete currentEntity[k];
|
|
627
672
|
currentEntity[k] = m; // put into the end of currentEntity
|
|
628
673
|
}
|
|
629
674
|
});
|
|
630
675
|
}
|
|
631
676
|
}
|
|
632
|
-
|
|
677
|
+
|
|
678
|
+
logger.debug(context, 'sendUpdateValueNgsi2 currentEntity sorted %j ', currentEntity);
|
|
633
679
|
let timestampValue = undefined;
|
|
634
680
|
// Loop for each final attribute to apply alias, multientity and expressions
|
|
635
681
|
for (const j in currentEntity) {
|
|
@@ -662,7 +708,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
662
708
|
// invalid mapping
|
|
663
709
|
logger.debug(
|
|
664
710
|
context,
|
|
665
|
-
'sendUpdateValueNgsi2
|
|
711
|
+
'sendUpdateValueNgsi2 invalid mapping for attr=%j newAttr=%j',
|
|
666
712
|
attr,
|
|
667
713
|
newAttr
|
|
668
714
|
);
|
|
@@ -670,12 +716,12 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
670
716
|
attr = undefined; // stop processing attr
|
|
671
717
|
newAttr = undefined;
|
|
672
718
|
} else {
|
|
673
|
-
ctxt[attr.name] = payload.entities[0][j]
|
|
719
|
+
ctxt[attr.name] = payload.entities[0][j].value;
|
|
674
720
|
}
|
|
675
721
|
}
|
|
676
722
|
logger.debug(
|
|
677
723
|
context,
|
|
678
|
-
'sendUpdateValueNgsi2
|
|
724
|
+
'sendUpdateValueNgsi2 procesing j=%j attr=%j ctxt=%j newAttr=%j ',
|
|
679
725
|
j,
|
|
680
726
|
attr,
|
|
681
727
|
ctxt,
|
|
@@ -689,7 +735,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
689
735
|
if (attr && attr.expression) {
|
|
690
736
|
logger.debug(
|
|
691
737
|
context,
|
|
692
|
-
'sendUpdateValueNgsi2
|
|
738
|
+
'sendUpdateValueNgsi2 apply expression=%j over ctxt=%j and device=%j',
|
|
693
739
|
attr.expression,
|
|
694
740
|
ctxt,
|
|
695
741
|
typeInformation
|
|
@@ -698,58 +744,43 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
698
744
|
try {
|
|
699
745
|
if (expressionPlugin.contextAvailable(attr.expression, ctxt, typeInformation)) {
|
|
700
746
|
res = expressionPlugin.applyExpression(attr.expression, ctxt, typeInformation);
|
|
747
|
+
if (
|
|
748
|
+
// By default undefined is equivalent to null: should not progress
|
|
749
|
+
(attr.skipValue === undefined && res === null) ||
|
|
750
|
+
(attr.skipValue !== undefined && res === attr.skipValue)
|
|
751
|
+
) {
|
|
752
|
+
logger.debug(
|
|
753
|
+
context,
|
|
754
|
+
'sendUpdateValueNgsi2 skip value=%j for res=%j with expression=%j',
|
|
755
|
+
attr.skipValue,
|
|
756
|
+
res,
|
|
757
|
+
attr.expression
|
|
758
|
+
);
|
|
759
|
+
delete payload.entities[0][j]; // remove measure attr
|
|
760
|
+
attr = undefined; // stop process attr
|
|
761
|
+
}
|
|
701
762
|
} else {
|
|
702
|
-
logger.
|
|
763
|
+
logger.info(
|
|
703
764
|
context,
|
|
704
|
-
'sendUpdateValueNgsi2
|
|
765
|
+
'sendUpdateValueNgsi2 no context available for apply expression=%j',
|
|
705
766
|
attr.expression
|
|
706
767
|
);
|
|
707
768
|
res = newAttr.value; // keep newAttr value
|
|
708
769
|
}
|
|
709
770
|
} catch (e) {
|
|
710
|
-
logger.error(context, 'sendUpdateValueNgsi2
|
|
711
|
-
if (
|
|
712
|
-
|
|
713
|
-
} else {
|
|
714
|
-
res = ctxt[attr.name]; // TBD: add reference to test
|
|
771
|
+
logger.error(context, 'sendUpdateValueNgsi2 apply expression exception=%j', e);
|
|
772
|
+
if (attr && attr.name) {
|
|
773
|
+
res = ctxt[attr.name];
|
|
715
774
|
}
|
|
716
775
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
!res.includes('NaN')
|
|
725
|
-
) {
|
|
726
|
-
newAttr.value = res;
|
|
727
|
-
if (newAttr.type === 'Number' && isFloat(newAttr.value)) {
|
|
728
|
-
newAttr.value = Number.parseFloat(newAttr.value);
|
|
729
|
-
} else if (newAttr.type === 'Number' && !Number.isNaN(Number.parseInt(newAttr.value))) {
|
|
730
|
-
newAttr.value = Number.parseInt(newAttr.value);
|
|
731
|
-
} else if (newAttr.type === 'Boolean') {
|
|
732
|
-
newAttr.value = newAttr.value === 'true' || newAttr.value === '1';
|
|
733
|
-
} else if (newAttr.type === 'None') {
|
|
734
|
-
newAttr.value = null;
|
|
735
|
-
}
|
|
736
|
-
} else if (isNaN(res) || res === 'undefined' || res === 'null') {
|
|
737
|
-
logger.debug(
|
|
738
|
-
context,
|
|
739
|
-
'sendUpdateValueNgsi2 \n apply expression result: isNaN || undefined || null'
|
|
740
|
-
);
|
|
741
|
-
if (res === 'null' && newAttr.type === 'None') {
|
|
742
|
-
newAttr.value = null;
|
|
743
|
-
} else {
|
|
744
|
-
delete payload.entities[0][j]; // remove measure attr
|
|
745
|
-
attr = undefined; // stop process attr
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
} else {
|
|
749
|
-
// jexl expression plugin
|
|
750
|
-
newAttr.value = res;
|
|
776
|
+
// jexl expression plugin
|
|
777
|
+
newAttr.value = res;
|
|
778
|
+
|
|
779
|
+
logger.debug(context, 'sendUpdateValueNgsi2 apply expression result=%j newAttr=%j', res, newAttr);
|
|
780
|
+
// update current context with value
|
|
781
|
+
if (attr && attr.name && ctxt[attr.name] !== undefined) {
|
|
782
|
+
ctxt[attr.name] = newAttr.value;
|
|
751
783
|
}
|
|
752
|
-
logger.debug(context, 'sendUpdateValueNgsi2 \n apply expression result %j \n newAttr %j', res, newAttr);
|
|
753
784
|
}
|
|
754
785
|
|
|
755
786
|
// Apply Multientity: entity_type and entity_name in attributes are in typeInformation.active
|
|
@@ -761,21 +792,21 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
761
792
|
if (expressionPlugin.contextAvailable(attr.entity_name, ctxt, typeInformation)) {
|
|
762
793
|
newEntityName = expressionPlugin.applyExpression(attr.entity_name, ctxt, typeInformation);
|
|
763
794
|
} else {
|
|
764
|
-
logger.
|
|
795
|
+
logger.info(
|
|
765
796
|
context,
|
|
766
|
-
'sendUpdateValueNgsi2
|
|
797
|
+
'sendUpdateValueNgsi2 MULTI no context available for apply expression=%j',
|
|
767
798
|
attr.entity_name
|
|
768
799
|
);
|
|
769
800
|
newEntityName = attr.entity_name;
|
|
770
801
|
}
|
|
771
802
|
newEntityName = newEntityName ? newEntityName : attr.entity_name;
|
|
772
803
|
} catch (e) {
|
|
773
|
-
logger.error(context, 'sendUpdateValueNgsi2
|
|
804
|
+
logger.error(context, 'sendUpdateValueNgsi2 MULTI apply expression exception=%j', e);
|
|
774
805
|
newEntityName = attr.entity_name;
|
|
775
806
|
}
|
|
776
807
|
logger.debug(
|
|
777
808
|
context,
|
|
778
|
-
'sendUpdateValueNgsi2
|
|
809
|
+
'sendUpdateValueNgsi2 MULTI apply expression=%j result=%j payload=%j',
|
|
779
810
|
attr.entity_name,
|
|
780
811
|
newEntityName,
|
|
781
812
|
payload
|
|
@@ -787,7 +818,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
787
818
|
type: attr.entity_type ? attr.entity_type : payload.entities[0].type
|
|
788
819
|
};
|
|
789
820
|
// Check if there is already a newEntity created
|
|
790
|
-
|
|
821
|
+
const alreadyEntity = payload.entities.find((entity) => {
|
|
791
822
|
return entity.id === newEntity.id && entity.type === newEntity.type;
|
|
792
823
|
});
|
|
793
824
|
if (alreadyEntity) {
|
|
@@ -804,7 +835,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
804
835
|
: timestampValue !== undefined
|
|
805
836
|
) {
|
|
806
837
|
newEntity = addTimestampNgsi2(newEntity, typeInformation.timezone, timestampValue);
|
|
807
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
838
|
+
logger.debug(context, 'sendUpdateValueNgsi2 timestamped newEntity=%j', newEntity);
|
|
808
839
|
}
|
|
809
840
|
payload.entities.push(newEntity);
|
|
810
841
|
}
|
|
@@ -812,7 +843,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
812
843
|
if (attr.name !== j) {
|
|
813
844
|
logger.debug(
|
|
814
845
|
context,
|
|
815
|
-
'sendUpdateValueNgsi2
|
|
846
|
+
'sendUpdateValueNgsi2 MULTI remove measure attr=%j keep alias j=%j from %j',
|
|
816
847
|
j,
|
|
817
848
|
attr,
|
|
818
849
|
payload
|
|
@@ -830,7 +861,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
830
861
|
}
|
|
831
862
|
}
|
|
832
863
|
if (newAttr && newAttr.type === constants.TIMESTAMP_TYPE_NGSI2 && newAttr.value) {
|
|
833
|
-
|
|
864
|
+
const extendedTime = compressTimestampPlugin.fromBasicToExtended(newAttr.value);
|
|
834
865
|
if (extendedTime) {
|
|
835
866
|
// TBD: there is not flag about compressTimestamp in iotagent-node-lib,
|
|
836
867
|
// but there is one in agents
|
|
@@ -842,7 +873,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
842
873
|
timestampValue = newAttr.value;
|
|
843
874
|
logger.debug(
|
|
844
875
|
context,
|
|
845
|
-
'sendUpdateValueNgsi2
|
|
876
|
+
'sendUpdateValueNgsi2 newAttr is TimeInstant and new payload=%j',
|
|
846
877
|
payload
|
|
847
878
|
);
|
|
848
879
|
}
|
|
@@ -854,7 +885,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
854
885
|
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].type === constants.TIMESTAMP_TYPE_NGSI2 &&
|
|
855
886
|
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].value
|
|
856
887
|
) {
|
|
857
|
-
|
|
888
|
+
const extendedTime = compressTimestampPlugin.fromBasicToExtended(
|
|
858
889
|
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].value
|
|
859
890
|
);
|
|
860
891
|
if (extendedTime) {
|
|
@@ -867,7 +898,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
867
898
|
// final attr loop
|
|
868
899
|
logger.debug(
|
|
869
900
|
context,
|
|
870
|
-
'sendUpdateValueNgsi2
|
|
901
|
+
'sendUpdateValueNgsi2 after procesing attr=%j current entity=%j current payload=%j',
|
|
871
902
|
j,
|
|
872
903
|
currentEntity,
|
|
873
904
|
payload
|
|
@@ -885,25 +916,15 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
885
916
|
) {
|
|
886
917
|
if (timestampValue) {
|
|
887
918
|
// timeInstant is provided as measure
|
|
888
|
-
if (payload.entities.length >
|
|
919
|
+
if (payload.entities.length > 0) {
|
|
889
920
|
for (let n = 0; n < payload.entities.length; n++) {
|
|
890
921
|
// include metadata with TimeInstant in attrs when TimeInstant is provided as measure in all entities
|
|
891
922
|
payload.entities[n] = addTimestampNgsi2(
|
|
892
923
|
payload.entities[n],
|
|
893
924
|
typeInformation.timezone,
|
|
894
|
-
timestampValue
|
|
895
|
-
false // skipMetadataAtt
|
|
925
|
+
timestampValue
|
|
896
926
|
);
|
|
897
927
|
}
|
|
898
|
-
} else {
|
|
899
|
-
// Do not include metadata with TimeInstant in attrs when TimeInstant is provided as measure
|
|
900
|
-
// and no more entities
|
|
901
|
-
payload.entities[0] = addTimestampNgsi2(
|
|
902
|
-
payload.entities[0],
|
|
903
|
-
typeInformation.timezone,
|
|
904
|
-
timestampValue,
|
|
905
|
-
true // skipMetadataAtt
|
|
906
|
-
);
|
|
907
928
|
}
|
|
908
929
|
} else {
|
|
909
930
|
// jshint maxdepth:5
|
|
@@ -921,10 +942,10 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
921
942
|
}
|
|
922
943
|
}
|
|
923
944
|
}
|
|
924
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
945
|
+
logger.debug(context, 'sendUpdateValueNgsi2 ending payload=%j', payload);
|
|
925
946
|
|
|
926
947
|
for (let m = 0; m < payload.entities.length; m++) {
|
|
927
|
-
for (
|
|
948
|
+
for (const key in payload.entities[m]) {
|
|
928
949
|
// purge object_id from payload
|
|
929
950
|
if (payload.entities[m][key] && payload.entities[m][key].object_id) {
|
|
930
951
|
delete payload.entities[m][key].object_id;
|
|
@@ -932,7 +953,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
932
953
|
}
|
|
933
954
|
payload.entities[m] = NGSIUtils.castJsonNativeAttributes(payload.entities[m]); // native types
|
|
934
955
|
}
|
|
935
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
956
|
+
logger.debug(context, 'sendUpdateValueNgsi2 payload with native types and without object_id=%j', payload);
|
|
936
957
|
|
|
937
958
|
options.json = payload;
|
|
938
959
|
|
|
@@ -981,6 +1002,10 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
981
1002
|
}
|
|
982
1003
|
|
|
983
1004
|
exports.sendQueryValue = sendQueryValueNgsi2;
|
|
984
|
-
exports.sendUpdateValue =
|
|
1005
|
+
exports.sendUpdateValue = function (entityName, attributes, typeInformation, token, callback) {
|
|
1006
|
+
NGSIUtils.applyMiddlewares(NGSIUtils.updateMiddleware, attributes, typeInformation, () => {
|
|
1007
|
+
return sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, callback);
|
|
1008
|
+
});
|
|
1009
|
+
};
|
|
985
1010
|
exports.addTimestamp = addTimestampNgsi2;
|
|
986
1011
|
exports.formatGeoAttrs = formatGeoAttrs;
|
|
@@ -181,6 +181,7 @@ function attributeToProvisioningAPIFormat(attribute) {
|
|
|
181
181
|
name: attribute.name,
|
|
182
182
|
type: attribute.type,
|
|
183
183
|
expression: attribute.expression,
|
|
184
|
+
skipValue: attribute.skipValue,
|
|
184
185
|
reverse: attribute.reverse,
|
|
185
186
|
entity_name: attribute.entity_name,
|
|
186
187
|
entity_type: attribute.entity_type,
|
|
@@ -352,20 +353,22 @@ function handleUpdateDevice(req, res, next) {
|
|
|
352
353
|
if (req.body.entity_name || req.body.entity_type) {
|
|
353
354
|
isTypeOrNameUpdated = true;
|
|
354
355
|
}
|
|
355
|
-
async.waterfall(
|
|
356
|
-
|
|
357
|
-
newDeviceUpdated
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
356
|
+
async.waterfall(
|
|
357
|
+
[apply(applyUpdatingHandler, newDevice)],
|
|
358
|
+
function handleUpdating(error, newDeviceUpdated) {
|
|
359
|
+
deviceService.updateRegister(
|
|
360
|
+
newDeviceUpdated,
|
|
361
|
+
isTypeOrNameUpdated,
|
|
362
|
+
function handleDeviceUpdate(error) {
|
|
363
|
+
if (error) {
|
|
364
|
+
next(error);
|
|
365
|
+
} else {
|
|
366
|
+
res.status(204).json({});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
);
|
|
369
372
|
} else {
|
|
370
373
|
next(new errors.DeviceNotFound(req.params.deviceId));
|
|
371
374
|
}
|
|
@@ -102,10 +102,13 @@
|
|
|
102
102
|
"description": "Optional expression for measurement transformation",
|
|
103
103
|
"type": "string"
|
|
104
104
|
},
|
|
105
|
+
"skipValue": {
|
|
106
|
+
"description": "Attribute is skipped when result of apply expression is this value",
|
|
107
|
+
"type": "any"
|
|
108
|
+
},
|
|
105
109
|
"entity_name": {
|
|
106
110
|
"description": "Optional entity name for multientity updates",
|
|
107
|
-
"type": "string"
|
|
108
|
-
"pattern": "^([^<>;'=\"]+)+$"
|
|
111
|
+
"type": "string"
|
|
109
112
|
},
|
|
110
113
|
"entity_type": {
|
|
111
114
|
"description": "Optional entity type for multientity updatess",
|