iotagent-node-lib 3.1.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/CHANGES_NEXT_RELEASE +0 -2
- 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/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 +13 -57
- package/lib/services/ngsi/entities-NGSI-v2.js +145 -108
- 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 +1 -1
- 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/ngsi-ld/expressions/jexlBasedTransformations-test.js +0 -2
- package/test/unit/ngsi-ld/plugins/bidirectional-plugin_test.js +8 -8
- package/test/unit/ngsi-ld/plugins/custom-plugin_test.js +152 -0
- package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +1 -1
- 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/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/expressions/jexlBasedTransformations-test.js +228 -6
- package/test/unit/ngsiv2/lazyAndCommands/polling-commands-test.js +1 -2
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +6 -6
- package/test/unit/ngsiv2/plugins/custom-plugin_test.js +151 -0
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +85 -12
- package/test/unit/ngsiv2/provisioning/device-provisioning-api_test.js +11 -3
- 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 -882
- package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -951
- package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -296
|
@@ -111,12 +111,10 @@ function addTimestampNgsi2(payload, timezone, timestampValue) {
|
|
|
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) {
|
|
@@ -325,16 +323,6 @@ function sendQueryValueNgsi2(entityName, attributes, typeInformation, token, cal
|
|
|
325
323
|
);
|
|
326
324
|
}
|
|
327
325
|
|
|
328
|
-
/**
|
|
329
|
-
* Determines if a value is of type float
|
|
330
|
-
*
|
|
331
|
-
* @param {String} value Value to be analyzed
|
|
332
|
-
* @return {boolean} True if float, False otherwise.
|
|
333
|
-
*/
|
|
334
|
-
function isFloat(value) {
|
|
335
|
-
return !isNaN(value) && value.toString().indexOf('.') !== -1;
|
|
336
|
-
}
|
|
337
|
-
|
|
338
326
|
/**
|
|
339
327
|
* Makes an update in the Device's entity in the context broker, with the values given in the 'attributes' array. This
|
|
340
328
|
* array should comply to the NGSIv2's attribute format.
|
|
@@ -347,12 +335,12 @@ function isFloat(value) {
|
|
|
347
335
|
function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, callback) {
|
|
348
336
|
logger.debug(
|
|
349
337
|
context,
|
|
350
|
-
'sendUpdateValueNgsi2 called with:
|
|
338
|
+
'sendUpdateValueNgsi2 called with: entityName=%s attributes=%j typeInformation=%j',
|
|
351
339
|
entityName,
|
|
352
340
|
attributes,
|
|
353
341
|
typeInformation
|
|
354
342
|
);
|
|
355
|
-
|
|
343
|
+
const payload = {
|
|
356
344
|
entities: [
|
|
357
345
|
{
|
|
358
346
|
id: entityName
|
|
@@ -382,8 +370,9 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
382
370
|
callback(new errors.TypeNotFound(null, entityName));
|
|
383
371
|
return;
|
|
384
372
|
}
|
|
373
|
+
|
|
385
374
|
let idTypeSSSList = pluginUtils.getIdTypeServSubServiceFromDevice(typeInformation);
|
|
386
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
375
|
+
logger.debug(context, 'sendUpdateValueNgsi2 idTypeSSS are %j ', idTypeSSSList);
|
|
387
376
|
let measureAttrsForCtxt = [];
|
|
388
377
|
|
|
389
378
|
// Check explicitAttrs: adds all final needed attributes to payload
|
|
@@ -408,7 +397,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
408
397
|
return;
|
|
409
398
|
}
|
|
410
399
|
}
|
|
411
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
400
|
+
logger.debug(context, 'sendUpdateValueNgsi2 pre-initial non-explicitAttrs payload=%j', payload);
|
|
412
401
|
// Loop for add attrs from type.information.active (and lazys?) into payload entities (entity[0])
|
|
413
402
|
if (typeInformation.active) {
|
|
414
403
|
typeInformation.active.forEach((attr) => {
|
|
@@ -435,7 +424,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
435
424
|
if (typeof typeInformation.explicitAttrs === 'string') {
|
|
436
425
|
// explicitAttrs is a jexlExpression
|
|
437
426
|
// This ctxt should include all possible attrs
|
|
438
|
-
|
|
427
|
+
const attributesCtxt = [];
|
|
439
428
|
if (typeInformation.static) {
|
|
440
429
|
typeInformation.static.forEach(function (att) {
|
|
441
430
|
attributesCtxt.push(att);
|
|
@@ -444,37 +433,57 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
444
433
|
// Measures
|
|
445
434
|
for (let i = 0; i < attributes.length; i++) {
|
|
446
435
|
if (attributes[i].name && attributes[i].type) {
|
|
447
|
-
|
|
436
|
+
const measureAttr = {
|
|
448
437
|
name: attributes[i].name,
|
|
449
438
|
value: attributes[i].value,
|
|
450
439
|
type: attributes[i].type
|
|
451
440
|
};
|
|
452
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
|
+
}
|
|
453
457
|
}
|
|
454
458
|
}
|
|
455
459
|
// This context is just to calculate explicitAttrs when is an expression
|
|
456
|
-
|
|
457
|
-
|
|
460
|
+
|
|
461
|
+
let ctxt = expressionPlugin.extractContext(attributesCtxt.concat(idTypeSSSList));
|
|
462
|
+
// typeInformation.active all attrs with expressions
|
|
458
463
|
if (typeInformation.active) {
|
|
459
464
|
typeInformation.active.forEach(function (att) {
|
|
460
|
-
if (att.expression) {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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,
|
|
465
476
|
type: att.type
|
|
466
477
|
};
|
|
467
|
-
attributesCtxt.push(
|
|
468
|
-
ctxt = expressionPlugin.extractContext(
|
|
469
|
-
attributesCtxt.concat(idTypeSSSList),
|
|
470
|
-
typeInformation
|
|
471
|
-
);
|
|
478
|
+
attributesCtxt.push(expandedAttrByObjectId);
|
|
472
479
|
}
|
|
480
|
+
ctxt = expressionPlugin.extractContext(attributesCtxt.concat(idTypeSSSList));
|
|
473
481
|
}
|
|
474
482
|
});
|
|
475
483
|
}
|
|
476
484
|
// calculate expression for explicitAttrs
|
|
477
485
|
try {
|
|
486
|
+
logger.debug(context, 'sendUpdateValueNgsi2 selectedAttrs ctxt %j', ctxt);
|
|
478
487
|
let res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctxt, typeInformation);
|
|
479
488
|
if (res === true) {
|
|
480
489
|
// like explicitAttrs == true
|
|
@@ -500,7 +509,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
500
509
|
// implies do nothing
|
|
501
510
|
logger.info(
|
|
502
511
|
context,
|
|
503
|
-
'sendUpdateValueNgsi2
|
|
512
|
+
'sendUpdateValueNgsi2 none selectedAttrs with %j and ctxt %j',
|
|
504
513
|
typeInformation.explicitAttrs,
|
|
505
514
|
ctxt
|
|
506
515
|
);
|
|
@@ -514,19 +523,42 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
514
523
|
if (selectedAttrs.includes(attr.name)) {
|
|
515
524
|
selectedAttrs.push(attr.object_id);
|
|
516
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
|
+
}
|
|
517
530
|
});
|
|
518
531
|
} else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
|
|
519
|
-
//
|
|
520
|
-
//
|
|
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'];
|
|
521
536
|
typeInformation.active.forEach((attr) => {
|
|
522
|
-
|
|
523
|
-
|
|
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
|
+
}
|
|
524
556
|
});
|
|
525
557
|
}
|
|
526
558
|
// This loop adds selected measured values (attributes) into payload entities (entity[0])
|
|
527
559
|
for (let i = 0; i < attributes.length; i++) {
|
|
528
560
|
if (attributes[i].name && selectedAttrs.includes(attributes[i].name) && attributes[i].type) {
|
|
529
|
-
|
|
561
|
+
const attr = typeInformation.active.find((obj) => {
|
|
530
562
|
return obj.name === attributes[i].name;
|
|
531
563
|
});
|
|
532
564
|
payload.entities[0][attributes[i].name] = {
|
|
@@ -542,7 +574,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
542
574
|
payload.entities[0][attributes[i].name].metadata = metadata;
|
|
543
575
|
}
|
|
544
576
|
} else if (attributes[i].name && !selectedAttrs.includes(attributes[i].name) && attributes[i].type) {
|
|
545
|
-
|
|
577
|
+
const att = {
|
|
546
578
|
name: attributes[i].name,
|
|
547
579
|
type: attributes[i].type,
|
|
548
580
|
value: attributes[i].value
|
|
@@ -552,10 +584,15 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
552
584
|
}
|
|
553
585
|
logger.debug(
|
|
554
586
|
context,
|
|
555
|
-
'sendUpdateValueNgsi2
|
|
587
|
+
'sendUpdateValueNgsi2 pre-initial explicitAttrs payload=%j selectedAttrs=%j',
|
|
556
588
|
payload,
|
|
557
589
|
selectedAttrs
|
|
558
590
|
);
|
|
591
|
+
let selectedAttrsByObjectId = selectedAttrs
|
|
592
|
+
.filter((o) => o !== undefined && o.object_id)
|
|
593
|
+
.map(function (el) {
|
|
594
|
+
return el.object_id;
|
|
595
|
+
});
|
|
559
596
|
|
|
560
597
|
// Loop for add seleted attrs from type.information.active into pyaload entities (entity[0])
|
|
561
598
|
if (typeInformation.active) {
|
|
@@ -577,13 +614,23 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
577
614
|
type: attr.type
|
|
578
615
|
};
|
|
579
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
|
+
};
|
|
580
627
|
}
|
|
581
628
|
});
|
|
582
629
|
}
|
|
583
630
|
} // END check explicitAttrs
|
|
584
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
631
|
+
logger.debug(context, 'sendUpdateValueNgsi2 initial payload=%j', payload);
|
|
585
632
|
|
|
586
|
-
|
|
633
|
+
const currentEntity = payload.entities[0];
|
|
587
634
|
|
|
588
635
|
// Prepare attributes for expresionPlugin
|
|
589
636
|
const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(currentEntity);
|
|
@@ -608,11 +655,11 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
608
655
|
}
|
|
609
656
|
attributesCtxt = attributesCtxt.concat(idTypeSSSList);
|
|
610
657
|
let ctxt = expressionPlugin.extractContext(attributesCtxt, typeInformation);
|
|
611
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
658
|
+
logger.debug(context, 'sendUpdateValueNgsi2 initial ctxt %j ', ctxt);
|
|
612
659
|
|
|
613
660
|
// Sort currentEntity to get first attrs without expressions (checking attrs in typeInformation.active)
|
|
614
661
|
// attributes without expressions should be processed before
|
|
615
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
662
|
+
logger.debug(context, 'sendUpdateValueNgsi2 currentEntity %j ', currentEntity);
|
|
616
663
|
if (typeInformation.active && typeInformation.active.length > 0) {
|
|
617
664
|
for (const k in currentEntity) {
|
|
618
665
|
typeInformation.active.forEach(function (att) {
|
|
@@ -620,14 +667,15 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
620
667
|
(att.object_id && att.object_id === k && att.expression) ||
|
|
621
668
|
(att.name && att.name === k && att.expression)
|
|
622
669
|
) {
|
|
623
|
-
|
|
670
|
+
const m = currentEntity[k];
|
|
624
671
|
delete currentEntity[k];
|
|
625
672
|
currentEntity[k] = m; // put into the end of currentEntity
|
|
626
673
|
}
|
|
627
674
|
});
|
|
628
675
|
}
|
|
629
676
|
}
|
|
630
|
-
|
|
677
|
+
|
|
678
|
+
logger.debug(context, 'sendUpdateValueNgsi2 currentEntity sorted %j ', currentEntity);
|
|
631
679
|
let timestampValue = undefined;
|
|
632
680
|
// Loop for each final attribute to apply alias, multientity and expressions
|
|
633
681
|
for (const j in currentEntity) {
|
|
@@ -660,7 +708,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
660
708
|
// invalid mapping
|
|
661
709
|
logger.debug(
|
|
662
710
|
context,
|
|
663
|
-
'sendUpdateValueNgsi2
|
|
711
|
+
'sendUpdateValueNgsi2 invalid mapping for attr=%j newAttr=%j',
|
|
664
712
|
attr,
|
|
665
713
|
newAttr
|
|
666
714
|
);
|
|
@@ -668,12 +716,12 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
668
716
|
attr = undefined; // stop processing attr
|
|
669
717
|
newAttr = undefined;
|
|
670
718
|
} else {
|
|
671
|
-
ctxt[attr.name] = payload.entities[0][j]
|
|
719
|
+
ctxt[attr.name] = payload.entities[0][j].value;
|
|
672
720
|
}
|
|
673
721
|
}
|
|
674
722
|
logger.debug(
|
|
675
723
|
context,
|
|
676
|
-
'sendUpdateValueNgsi2
|
|
724
|
+
'sendUpdateValueNgsi2 procesing j=%j attr=%j ctxt=%j newAttr=%j ',
|
|
677
725
|
j,
|
|
678
726
|
attr,
|
|
679
727
|
ctxt,
|
|
@@ -687,7 +735,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
687
735
|
if (attr && attr.expression) {
|
|
688
736
|
logger.debug(
|
|
689
737
|
context,
|
|
690
|
-
'sendUpdateValueNgsi2
|
|
738
|
+
'sendUpdateValueNgsi2 apply expression=%j over ctxt=%j and device=%j',
|
|
691
739
|
attr.expression,
|
|
692
740
|
ctxt,
|
|
693
741
|
typeInformation
|
|
@@ -696,58 +744,43 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
696
744
|
try {
|
|
697
745
|
if (expressionPlugin.contextAvailable(attr.expression, ctxt, typeInformation)) {
|
|
698
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
|
+
}
|
|
699
762
|
} else {
|
|
700
|
-
logger.
|
|
763
|
+
logger.info(
|
|
701
764
|
context,
|
|
702
|
-
'sendUpdateValueNgsi2
|
|
765
|
+
'sendUpdateValueNgsi2 no context available for apply expression=%j',
|
|
703
766
|
attr.expression
|
|
704
767
|
);
|
|
705
768
|
res = newAttr.value; // keep newAttr value
|
|
706
769
|
}
|
|
707
770
|
} catch (e) {
|
|
708
|
-
logger.error(context, 'sendUpdateValueNgsi2
|
|
709
|
-
if (
|
|
710
|
-
|
|
711
|
-
} else {
|
|
712
|
-
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];
|
|
713
774
|
}
|
|
714
775
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
!res.includes('NaN')
|
|
723
|
-
) {
|
|
724
|
-
newAttr.value = res;
|
|
725
|
-
if (newAttr.type === 'Number' && isFloat(newAttr.value)) {
|
|
726
|
-
newAttr.value = Number.parseFloat(newAttr.value);
|
|
727
|
-
} else if (newAttr.type === 'Number' && !Number.isNaN(Number.parseInt(newAttr.value))) {
|
|
728
|
-
newAttr.value = Number.parseInt(newAttr.value);
|
|
729
|
-
} else if (newAttr.type === 'Boolean') {
|
|
730
|
-
newAttr.value = newAttr.value === 'true' || newAttr.value === '1';
|
|
731
|
-
} else if (newAttr.type === 'None') {
|
|
732
|
-
newAttr.value = null;
|
|
733
|
-
}
|
|
734
|
-
} else if (isNaN(res) || res === 'undefined' || res === 'null') {
|
|
735
|
-
logger.debug(
|
|
736
|
-
context,
|
|
737
|
-
'sendUpdateValueNgsi2 \n apply expression result: isNaN || undefined || null'
|
|
738
|
-
);
|
|
739
|
-
if (res === 'null' && newAttr.type === 'None') {
|
|
740
|
-
newAttr.value = null;
|
|
741
|
-
} else {
|
|
742
|
-
delete payload.entities[0][j]; // remove measure attr
|
|
743
|
-
attr = undefined; // stop process attr
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
} else {
|
|
747
|
-
// jexl expression plugin
|
|
748
|
-
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;
|
|
749
783
|
}
|
|
750
|
-
logger.debug(context, 'sendUpdateValueNgsi2 \n apply expression result %j \n newAttr %j', res, newAttr);
|
|
751
784
|
}
|
|
752
785
|
|
|
753
786
|
// Apply Multientity: entity_type and entity_name in attributes are in typeInformation.active
|
|
@@ -759,21 +792,21 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
759
792
|
if (expressionPlugin.contextAvailable(attr.entity_name, ctxt, typeInformation)) {
|
|
760
793
|
newEntityName = expressionPlugin.applyExpression(attr.entity_name, ctxt, typeInformation);
|
|
761
794
|
} else {
|
|
762
|
-
logger.
|
|
795
|
+
logger.info(
|
|
763
796
|
context,
|
|
764
|
-
'sendUpdateValueNgsi2
|
|
797
|
+
'sendUpdateValueNgsi2 MULTI no context available for apply expression=%j',
|
|
765
798
|
attr.entity_name
|
|
766
799
|
);
|
|
767
800
|
newEntityName = attr.entity_name;
|
|
768
801
|
}
|
|
769
802
|
newEntityName = newEntityName ? newEntityName : attr.entity_name;
|
|
770
803
|
} catch (e) {
|
|
771
|
-
logger.error(context, 'sendUpdateValueNgsi2
|
|
804
|
+
logger.error(context, 'sendUpdateValueNgsi2 MULTI apply expression exception=%j', e);
|
|
772
805
|
newEntityName = attr.entity_name;
|
|
773
806
|
}
|
|
774
807
|
logger.debug(
|
|
775
808
|
context,
|
|
776
|
-
'sendUpdateValueNgsi2
|
|
809
|
+
'sendUpdateValueNgsi2 MULTI apply expression=%j result=%j payload=%j',
|
|
777
810
|
attr.entity_name,
|
|
778
811
|
newEntityName,
|
|
779
812
|
payload
|
|
@@ -785,7 +818,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
785
818
|
type: attr.entity_type ? attr.entity_type : payload.entities[0].type
|
|
786
819
|
};
|
|
787
820
|
// Check if there is already a newEntity created
|
|
788
|
-
|
|
821
|
+
const alreadyEntity = payload.entities.find((entity) => {
|
|
789
822
|
return entity.id === newEntity.id && entity.type === newEntity.type;
|
|
790
823
|
});
|
|
791
824
|
if (alreadyEntity) {
|
|
@@ -802,7 +835,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
802
835
|
: timestampValue !== undefined
|
|
803
836
|
) {
|
|
804
837
|
newEntity = addTimestampNgsi2(newEntity, typeInformation.timezone, timestampValue);
|
|
805
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
838
|
+
logger.debug(context, 'sendUpdateValueNgsi2 timestamped newEntity=%j', newEntity);
|
|
806
839
|
}
|
|
807
840
|
payload.entities.push(newEntity);
|
|
808
841
|
}
|
|
@@ -810,7 +843,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
810
843
|
if (attr.name !== j) {
|
|
811
844
|
logger.debug(
|
|
812
845
|
context,
|
|
813
|
-
'sendUpdateValueNgsi2
|
|
846
|
+
'sendUpdateValueNgsi2 MULTI remove measure attr=%j keep alias j=%j from %j',
|
|
814
847
|
j,
|
|
815
848
|
attr,
|
|
816
849
|
payload
|
|
@@ -828,7 +861,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
828
861
|
}
|
|
829
862
|
}
|
|
830
863
|
if (newAttr && newAttr.type === constants.TIMESTAMP_TYPE_NGSI2 && newAttr.value) {
|
|
831
|
-
|
|
864
|
+
const extendedTime = compressTimestampPlugin.fromBasicToExtended(newAttr.value);
|
|
832
865
|
if (extendedTime) {
|
|
833
866
|
// TBD: there is not flag about compressTimestamp in iotagent-node-lib,
|
|
834
867
|
// but there is one in agents
|
|
@@ -840,7 +873,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
840
873
|
timestampValue = newAttr.value;
|
|
841
874
|
logger.debug(
|
|
842
875
|
context,
|
|
843
|
-
'sendUpdateValueNgsi2
|
|
876
|
+
'sendUpdateValueNgsi2 newAttr is TimeInstant and new payload=%j',
|
|
844
877
|
payload
|
|
845
878
|
);
|
|
846
879
|
}
|
|
@@ -852,7 +885,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
852
885
|
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].type === constants.TIMESTAMP_TYPE_NGSI2 &&
|
|
853
886
|
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].value
|
|
854
887
|
) {
|
|
855
|
-
|
|
888
|
+
const extendedTime = compressTimestampPlugin.fromBasicToExtended(
|
|
856
889
|
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].value
|
|
857
890
|
);
|
|
858
891
|
if (extendedTime) {
|
|
@@ -865,7 +898,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
865
898
|
// final attr loop
|
|
866
899
|
logger.debug(
|
|
867
900
|
context,
|
|
868
|
-
'sendUpdateValueNgsi2
|
|
901
|
+
'sendUpdateValueNgsi2 after procesing attr=%j current entity=%j current payload=%j',
|
|
869
902
|
j,
|
|
870
903
|
currentEntity,
|
|
871
904
|
payload
|
|
@@ -909,10 +942,10 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
909
942
|
}
|
|
910
943
|
}
|
|
911
944
|
}
|
|
912
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
945
|
+
logger.debug(context, 'sendUpdateValueNgsi2 ending payload=%j', payload);
|
|
913
946
|
|
|
914
947
|
for (let m = 0; m < payload.entities.length; m++) {
|
|
915
|
-
for (
|
|
948
|
+
for (const key in payload.entities[m]) {
|
|
916
949
|
// purge object_id from payload
|
|
917
950
|
if (payload.entities[m][key] && payload.entities[m][key].object_id) {
|
|
918
951
|
delete payload.entities[m][key].object_id;
|
|
@@ -920,7 +953,7 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
920
953
|
}
|
|
921
954
|
payload.entities[m] = NGSIUtils.castJsonNativeAttributes(payload.entities[m]); // native types
|
|
922
955
|
}
|
|
923
|
-
logger.debug(context, 'sendUpdateValueNgsi2
|
|
956
|
+
logger.debug(context, 'sendUpdateValueNgsi2 payload with native types and without object_id=%j', payload);
|
|
924
957
|
|
|
925
958
|
options.json = payload;
|
|
926
959
|
|
|
@@ -969,6 +1002,10 @@ function sendUpdateValueNgsi2(entityName, attributes, typeInformation, token, ca
|
|
|
969
1002
|
}
|
|
970
1003
|
|
|
971
1004
|
exports.sendQueryValue = sendQueryValueNgsi2;
|
|
972
|
-
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
|
+
};
|
|
973
1010
|
exports.addTimestamp = addTimestampNgsi2;
|
|
974
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",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"expressionLanguage": {
|
|
40
40
|
"description": "Expression language used to apply expressions for this device",
|
|
41
|
-
"type": "
|
|
41
|
+
"type": "string"
|
|
42
42
|
},
|
|
43
43
|
"explicitAttrs": {
|
|
44
44
|
"description": "Flag about only provisioned attributes will be processed to Context Broker"
|
|
@@ -98,13 +98,15 @@
|
|
|
98
98
|
},
|
|
99
99
|
"expression": {
|
|
100
100
|
"description": "Optional expression for measurement transformation",
|
|
101
|
-
"type": "string"
|
|
102
|
-
|
|
101
|
+
"type": "string"
|
|
102
|
+
},
|
|
103
|
+
"skipValue": {
|
|
104
|
+
"description": "Attribute is skipped when result of apply expression is this value",
|
|
105
|
+
"type": "any"
|
|
103
106
|
},
|
|
104
107
|
"entity_name": {
|
|
105
108
|
"description": "Optional entity name for multientity updates",
|
|
106
|
-
"type": "string"
|
|
107
|
-
"pattern": "^([^<>;'=\"]+)+$"
|
|
109
|
+
"type": "string"
|
|
108
110
|
},
|
|
109
111
|
"entity_type": {
|
|
110
112
|
"description": "Optional entity type for multientity updatess",
|
|
@@ -88,10 +88,13 @@
|
|
|
88
88
|
"description": "Optional expression for measurement transformation",
|
|
89
89
|
"type": "string"
|
|
90
90
|
},
|
|
91
|
+
"skipValue": {
|
|
92
|
+
"description": "Attribute is skipped when result of apply expression is this value",
|
|
93
|
+
"type": "any"
|
|
94
|
+
},
|
|
91
95
|
"entity_name": {
|
|
92
96
|
"description": "Optional entity name for multientity updates",
|
|
93
|
-
"type": "string"
|
|
94
|
-
"pattern": "^([^<>;'=\"]+)+$"
|
|
97
|
+
"type": "string"
|
|
95
98
|
},
|
|
96
99
|
"entity_type": {
|
|
97
100
|
"description": "Optional entity type for multientity updatess",
|
|
@@ -79,13 +79,11 @@
|
|
|
79
79
|
},
|
|
80
80
|
"expression": {
|
|
81
81
|
"description": "Optional expression for measurement transformation",
|
|
82
|
-
"type": "string"
|
|
83
|
-
"pattern": "^([^<>;'=]+)+$"
|
|
82
|
+
"type": "string"
|
|
84
83
|
},
|
|
85
84
|
"entity_name": {
|
|
86
85
|
"description": "Optional entity name for multientity updates",
|
|
87
|
-
"type": "string"
|
|
88
|
-
"pattern": "^([^<>;'=\"]+)+$"
|
|
86
|
+
"type": "string"
|
|
89
87
|
},
|
|
90
88
|
"entity_type": {
|
|
91
89
|
"description": "Optional entity type for multientity updatess",
|
|
@@ -158,4 +156,4 @@
|
|
|
158
156
|
"type": "array"
|
|
159
157
|
}
|
|
160
158
|
}
|
|
161
|
-
}
|
|
159
|
+
}
|