iotagent-node-lib 2.26.0 → 3.0.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/lib/fiware-iotagent-lib.js +0 -5
- package/lib/plugins/compressTimestamp.js +2 -7
- package/lib/plugins/expressionParser.js +0 -47
- package/lib/plugins/expressionPlugin.js +6 -78
- package/lib/plugins/jexlParser.js +0 -36
- package/lib/plugins/pluginUtils.js +0 -119
- package/lib/services/ngsi/entities-NGSI-LD.js +600 -199
- package/lib/services/ngsi/entities-NGSI-v2.js +620 -298
- package/lib/services/ngsi/ngsiUtils.js +15 -22
- package/package.json +1 -1
- package/test/unit/general/loglevel-api_test.js +0 -2
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAliasPlugin1.json +0 -5
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast10.json +1 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast8.json +1 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextAutocast9.json +1 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin32.json +16 -15
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4.json +8 -8
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextExpressionPlugin4a.json +34 -34
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin5.json +8 -8
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin6.json +0 -20
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin7.json +0 -5
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityPlugin8.json +10 -10
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +8 -7
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +5 -4
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextMultientityTimestampPlugin3.json +1 -1
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextProcessTimestamp.json +4 -4
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverride.json +4 -3
- package/test/unit/ngsi-ld/examples/contextRequests/updateContextTimestampOverrideWithoutMilis.json +4 -3
- package/test/unit/ngsi-ld/expressions/expressionBasedTransformations-test.js +2 -5
- package/test/unit/ngsi-ld/expressions/jexlBasedTransformations-test.js +1 -7
- package/test/unit/ngsi-ld/ngsiService/autocast-test.js +15 -3
- package/test/unit/ngsi-ld/plugins/alias-plugin_test.js +0 -2
- package/test/unit/ngsi-ld/plugins/compress-timestamp-plugin_test.js +0 -31
- package/test/unit/ngsi-ld/plugins/multientity-plugin_test.js +18 -18
- package/test/unit/ngsi-ld/plugins/timestamp-processing-plugin_test.js +19 -6
- package/test/unit/ngsiv2/examples/contextRequests/updateContextAliasPlugin1.json +2 -2
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin11.json +5 -1
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin5.json +4 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin6.json +0 -16
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin7.json +0 -4
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityPlugin8.json +8 -8
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin1.json +42 -42
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin2.json +7 -7
- package/test/unit/ngsiv2/examples/contextRequests/updateContextMultientityTimestampPlugin4.json +0 -5
- package/test/unit/ngsiv2/examples/contextRequests/updateContextProcessTimestamp.json +1 -7
- package/test/unit/ngsiv2/expressions/expressionBasedTransformations-test.js +0 -3
- package/test/unit/ngsiv2/expressions/expressionCombinedTransformations-test.js +0 -6
- package/test/unit/ngsiv2/expressions/jexlBasedTransformations-test.js +1 -7
- package/test/unit/ngsiv2/plugins/alias-plugin_test.js +0 -2
- package/test/unit/ngsiv2/plugins/bidirectional-plugin_test.js +0 -4
- package/test/unit/ngsiv2/plugins/compress-timestamp-plugin_test.js +0 -32
- package/test/unit/ngsiv2/plugins/multientity-plugin_test.js +5 -15
- package/test/unit/ngsiv2/plugins/timestamp-processing-plugin_test.js +1 -1
- package/lib/plugins/addEvent.js +0 -32
- package/lib/plugins/attributeAlias.js +0 -107
- package/lib/plugins/multiEntity.js +0 -255
- package/lib/plugins/timestampProcessPlugin.js +0 -95
- package/test/unit/ngsi-ld/plugins/event-plugin_test.js +0 -116
- package/test/unit/ngsiv2/plugins/event-plugin_test.js +0 -118
- package/test/unit/ngsiv2/plugins/translation-inPlugins_test.js +0 -262
|
@@ -26,20 +26,20 @@
|
|
|
26
26
|
/* eslint-disable consistent-return */
|
|
27
27
|
|
|
28
28
|
const request = require('../../request-shim');
|
|
29
|
-
const statsService = require('./../stats/statsRegistry');
|
|
30
|
-
const async = require('async');
|
|
31
|
-
const apply = async.apply;
|
|
32
29
|
const alarms = require('../common/alarmManagement');
|
|
33
30
|
const errors = require('../../errors');
|
|
34
31
|
const utils = require('../northBound/restUtils');
|
|
32
|
+
const pluginUtils = require('../../plugins/pluginUtils');
|
|
35
33
|
const config = require('../../commonConfig');
|
|
36
34
|
const constants = require('../../constants');
|
|
37
35
|
const jexlParser = require('../../plugins/jexlParser');
|
|
36
|
+
const expressionPlugin = require('../../plugins/expressionPlugin');
|
|
37
|
+
const compressTimestampPlugin = require('../../plugins/compressTimestamp');
|
|
38
38
|
const moment = require('moment-timezone');
|
|
39
39
|
const logger = require('logops');
|
|
40
40
|
const _ = require('underscore');
|
|
41
41
|
const context = {
|
|
42
|
-
op: 'IoTAgentNGSI
|
|
42
|
+
op: 'IoTAgentNGSI-LD'
|
|
43
43
|
};
|
|
44
44
|
const NGSIv2 = require('./entities-NGSI-v2');
|
|
45
45
|
const NGSIUtils = require('./ngsiUtils');
|
|
@@ -171,7 +171,7 @@ function convertAttrNGSILD(attr) {
|
|
|
171
171
|
obj.value = { '@type': attr.type, '@value': attr.value };
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
if (attr.metadata) {
|
|
174
|
+
if (!!obj && attr.metadata) {
|
|
175
175
|
let timestamp;
|
|
176
176
|
Object.keys(attr.metadata).forEach(function (key) {
|
|
177
177
|
switch (key) {
|
|
@@ -411,89 +411,13 @@ function addLinkedEntities(typeInformation, json) {
|
|
|
411
411
|
}
|
|
412
412
|
|
|
413
413
|
/**
|
|
414
|
-
*
|
|
414
|
+
* Determines if a value is of type float
|
|
415
415
|
*
|
|
416
|
-
* @param
|
|
417
|
-
* @
|
|
416
|
+
* @param {String} value Value to be analyzed
|
|
417
|
+
* @return {boolean} True if float, False otherwise.
|
|
418
418
|
*/
|
|
419
|
-
function
|
|
420
|
-
|
|
421
|
-
if (typeInformation.explicitAttrs) {
|
|
422
|
-
explicitAttrsList = [];
|
|
423
|
-
explicitAttrsList.push('type');
|
|
424
|
-
explicitAttrsList.push('id');
|
|
425
|
-
if (typeof typeInformation.explicitAttrs === 'boolean') {
|
|
426
|
-
if (typeInformation.timestamp) {
|
|
427
|
-
explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
|
|
428
|
-
}
|
|
429
|
-
if (typeInformation.active) {
|
|
430
|
-
typeInformation.active.forEach((attr) => {
|
|
431
|
-
explicitAttrsList.push(attr.name);
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
if (typeInformation.staticAttributes) {
|
|
435
|
-
typeInformation.staticAttributes.forEach((attr) => {
|
|
436
|
-
explicitAttrsList.push(attr.name);
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
} else if (typeof typeInformation.explicitAttrs === 'string') {
|
|
440
|
-
entities.forEach((entity) => {
|
|
441
|
-
const attsArray = utils.extractAttributesArrayFromNgsi2Entity(entity);
|
|
442
|
-
const ctx = jexlParser.extractContext(attsArray);
|
|
443
|
-
const res = jexlParser.parse(typeInformation.explicitAttrs, ctx);
|
|
444
|
-
explicitAttrsList = explicitAttrsList.concat(res);
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
if (explicitAttrsList && explicitAttrsList.length >= 0) {
|
|
449
|
-
entities.forEach((entity) => {
|
|
450
|
-
const hidden = _.difference(_.keys(entity), explicitAttrsList);
|
|
451
|
-
hidden.forEach((attr) => {
|
|
452
|
-
delete entity[attr];
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
return entities;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Remove id, type and any hidden attrs after processing
|
|
461
|
-
*
|
|
462
|
-
* @param {Object} result Unprocessed entity
|
|
463
|
-
* @param {Object} typeInformation Configuration information for the device.
|
|
464
|
-
*/
|
|
465
|
-
function removeHiddenAttrs(result, typeInformation) {
|
|
466
|
-
delete result.id;
|
|
467
|
-
delete result.type;
|
|
468
|
-
let explicitAttrsList;
|
|
469
|
-
if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
|
|
470
|
-
explicitAttrsList = [];
|
|
471
|
-
if (typeInformation.timestamp) {
|
|
472
|
-
explicitAttrsList.push(constants.TIMESTAMP_ATTRIBUTE);
|
|
473
|
-
}
|
|
474
|
-
if (typeInformation.active) {
|
|
475
|
-
typeInformation.active.forEach((attr) => {
|
|
476
|
-
explicitAttrsList.push(attr.name);
|
|
477
|
-
});
|
|
478
|
-
}
|
|
479
|
-
if (typeInformation.staticAttributes) {
|
|
480
|
-
typeInformation.staticAttributes.forEach((attr) => {
|
|
481
|
-
explicitAttrsList.push(attr.name);
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
} else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'string') {
|
|
485
|
-
const attsArray = utils.extractAttributesArrayFromNgsi2Entity(result);
|
|
486
|
-
const ctx = jexlParser.extractContext(attsArray);
|
|
487
|
-
const res = jexlParser.parse(typeInformation.explicitAttrs, ctx);
|
|
488
|
-
explicitAttrsList = res;
|
|
489
|
-
}
|
|
490
|
-
if (explicitAttrsList && explicitAttrsList.length >= 0) {
|
|
491
|
-
const hidden = _.difference(_.keys(result), explicitAttrsList);
|
|
492
|
-
hidden.forEach((attr) => {
|
|
493
|
-
delete result[attr];
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
return result;
|
|
419
|
+
function isFloat(value) {
|
|
420
|
+
return !isNaN(value) && value.toString().indexOf('.') !== -1;
|
|
497
421
|
}
|
|
498
422
|
|
|
499
423
|
/**
|
|
@@ -506,9 +430,31 @@ function removeHiddenAttrs(result, typeInformation) {
|
|
|
506
430
|
* @param {String} token User token to identify against the PEP Proxies (optional).
|
|
507
431
|
*/
|
|
508
432
|
function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, callback) {
|
|
509
|
-
|
|
433
|
+
logger.debug(
|
|
434
|
+
context,
|
|
435
|
+
'sendUpdateValueNgsiLD called with: \n entityName=%s \n attributes=%j \n typeInformation=%j',
|
|
436
|
+
entityName,
|
|
437
|
+
attributes,
|
|
438
|
+
typeInformation
|
|
439
|
+
);
|
|
440
|
+
const payload = [
|
|
441
|
+
{
|
|
442
|
+
id: entityName
|
|
443
|
+
}
|
|
444
|
+
];
|
|
445
|
+
|
|
510
446
|
const url = '/ngsi-ld/v1/entityOperations/upsert/?options=update';
|
|
511
447
|
|
|
448
|
+
if (typeInformation && typeInformation.type) {
|
|
449
|
+
payload[0].type = typeInformation.type;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (config.getConfig().appendMode === true) {
|
|
453
|
+
payload.actionType = 'append';
|
|
454
|
+
} else {
|
|
455
|
+
payload.actionType = 'update';
|
|
456
|
+
}
|
|
457
|
+
|
|
512
458
|
const options = NGSIUtils.createRequestObject(url, typeInformation, token);
|
|
513
459
|
options.method = 'POST';
|
|
514
460
|
|
|
@@ -520,143 +466,598 @@ function sendUpdateValueNgsiLD(entityName, attributes, typeInformation, token, c
|
|
|
520
466
|
callback(new errors.TypeNotFound(null, entityName));
|
|
521
467
|
return;
|
|
522
468
|
}
|
|
469
|
+
const idTypeSSSList = pluginUtils.getIdTypeServSubServiceFromDevice(typeInformation);
|
|
470
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n idTypeSSS are %j ', idTypeSSSList);
|
|
471
|
+
const measureAttrsForCtxt = [];
|
|
523
472
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
473
|
+
// Check explicitAttrs: adds all final needed attributes to payload
|
|
474
|
+
if (
|
|
475
|
+
typeInformation.explicitAttrs === undefined ||
|
|
476
|
+
(typeof typeInformation.explicitAttrs === 'boolean' && !typeInformation.explicitAttrs)
|
|
477
|
+
// explicitAttrs is not defined => default case: all attrs should be included
|
|
478
|
+
) {
|
|
479
|
+
// This loop adds all measure values (attributes) into payload entities (entity[0])
|
|
480
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
481
|
+
if (attributes[i].name && attributes[i].type) {
|
|
482
|
+
payload[0][attributes[i].name] = {
|
|
483
|
+
value: attributes[i].value,
|
|
484
|
+
type: attributes[i].type
|
|
485
|
+
};
|
|
486
|
+
const metadata = NGSIUtils.getMetaData(typeInformation, attributes[i].name, attributes[i].metadata);
|
|
487
|
+
if (metadata) {
|
|
488
|
+
payload[0][attributes[i].name].metadata = metadata;
|
|
489
|
+
}
|
|
490
|
+
} else {
|
|
491
|
+
callback(new errors.BadRequest(null, entityName));
|
|
492
|
+
return;
|
|
536
493
|
}
|
|
537
|
-
} else {
|
|
538
|
-
callback(new errors.BadRequest(null, entityName));
|
|
539
|
-
return;
|
|
540
494
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
} else {
|
|
553
|
-
if (result) {
|
|
554
|
-
// The payload has been transformed by multientity plugin. It is not a JSON object but an Array.
|
|
555
|
-
if (result instanceof Array) {
|
|
556
|
-
if (
|
|
557
|
-
'timestamp' in typeInformation && typeInformation.timestamp !== undefined
|
|
558
|
-
? typeInformation.timestamp
|
|
559
|
-
: config.getConfig().timestamp
|
|
560
|
-
) {
|
|
561
|
-
if (!utils.isTimestampedNgsi2(result)) {
|
|
562
|
-
options.json = NGSIv2.addTimestamp(result, typeInformation.timezone);
|
|
563
|
-
} else if (!utils.IsValidTimestampedNgsi2(result)) {
|
|
564
|
-
logger.error(context, 'Invalid timestamp:%s', JSON.stringify(result));
|
|
565
|
-
callback(new errors.BadTimestamp(result));
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
options.json = removeHiddenAttrsFromMultiEntity(result, typeInformation);
|
|
495
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n pre-initial non-explicitAttrs payload=%j', payload);
|
|
496
|
+
// Loop for add attrs from type.information.active (and lazys?) into payload entities (entity[0])
|
|
497
|
+
if (typeInformation.active) {
|
|
498
|
+
typeInformation.active.forEach((attr) => {
|
|
499
|
+
if (attr.expression) {
|
|
500
|
+
if (attr.object_id) {
|
|
501
|
+
payload[0][attr.object_id] = {
|
|
502
|
+
value: payload[0][attr.object_id] ? payload[0][attr.object_id].value : undefined,
|
|
503
|
+
type: attr.type,
|
|
504
|
+
object_id: attr.object_id
|
|
505
|
+
};
|
|
571
506
|
} else {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
'timestamp' in typeInformation && typeInformation.timestamp !== undefined
|
|
577
|
-
? typeInformation.timestamp
|
|
578
|
-
: config.getConfig().timestamp
|
|
579
|
-
) {
|
|
580
|
-
if (!utils.isTimestampedNgsi2(options.json)) {
|
|
581
|
-
options.json = NGSIv2.addTimestamp(options.json, typeInformation.timezone);
|
|
582
|
-
} else if (!utils.IsValidTimestampedNgsi2(options.json)) {
|
|
583
|
-
logger.error(context, 'Invalid timestamp:%s', JSON.stringify(options.json));
|
|
584
|
-
callback(new errors.BadTimestamp(options.json));
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
507
|
+
payload[0][attr.name] = {
|
|
508
|
+
value: payload[0][attr.name] ? payload[0][attr.name].value : undefined,
|
|
509
|
+
type: attr.type
|
|
510
|
+
};
|
|
588
511
|
}
|
|
589
|
-
} else {
|
|
590
|
-
delete payload.id;
|
|
591
|
-
delete payload.type;
|
|
592
|
-
options.json = payload;
|
|
593
512
|
}
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
let selectedAttrs = [];
|
|
517
|
+
if (typeof typeInformation.explicitAttrs === 'string') {
|
|
518
|
+
// explicitAttrs is a jexlExpression
|
|
519
|
+
// This ctxt should include all possible attrs
|
|
520
|
+
const attributesCtxt = [];
|
|
521
|
+
if (typeInformation.static) {
|
|
522
|
+
typeInformation.static.forEach(function (att) {
|
|
523
|
+
attributesCtxt.push(att);
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
// Measures
|
|
527
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
528
|
+
if (attributes[i].name && attributes[i].type) {
|
|
529
|
+
const measureAttr = {
|
|
530
|
+
name: attributes[i].name,
|
|
531
|
+
value: attributes[i].value,
|
|
532
|
+
type: attributes[i].type
|
|
533
|
+
};
|
|
534
|
+
attributesCtxt.push(measureAttr);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// This context is just to calculate explicitAttrs when is an expression
|
|
538
|
+
let ctxt = expressionPlugin.extractContext(attributesCtxt.concat(idTypeSSSList), typeInformation);
|
|
539
|
+
// typeInformation.active attrs with expressions expanded by current ctxt
|
|
540
|
+
if (typeInformation.active) {
|
|
541
|
+
typeInformation.active.forEach(function (att) {
|
|
542
|
+
if (att.expression) {
|
|
543
|
+
if (expressionPlugin.contextAvailable(att.expression, ctxt, typeInformation)) {
|
|
544
|
+
const expandedAttr = {
|
|
545
|
+
name: att.name,
|
|
546
|
+
value: att.expression, // it doesn't matter final value here
|
|
547
|
+
type: att.type
|
|
548
|
+
};
|
|
549
|
+
attributesCtxt.push(expandedAttr);
|
|
550
|
+
ctxt = expressionPlugin.extractContext(
|
|
551
|
+
attributesCtxt.concat(idTypeSSSList),
|
|
552
|
+
typeInformation
|
|
553
|
+
);
|
|
612
554
|
}
|
|
613
555
|
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
// calculate expression for explicitAttrs
|
|
559
|
+
try {
|
|
560
|
+
const res = jexlParser.applyExpression(typeInformation.explicitAttrs, ctxt, typeInformation);
|
|
561
|
+
if (res === true) {
|
|
562
|
+
// like explicitAttrs == true
|
|
563
|
+
// selectAttrs should be measures which are defined attributes
|
|
564
|
+
typeInformation.active.forEach((attr) => {
|
|
565
|
+
selectedAttrs.push(attr.name);
|
|
566
|
+
selectedAttrs.push(attr.object_id);
|
|
567
|
+
});
|
|
568
|
+
} else if (res === false) {
|
|
569
|
+
// like explicitAttrs == false
|
|
570
|
+
// selectAttrs should be measures and defined attributes
|
|
571
|
+
typeInformation.active.forEach((attr) => {
|
|
572
|
+
selectedAttrs.push(attr.name);
|
|
573
|
+
selectedAttrs.push(attr.object_id);
|
|
574
|
+
});
|
|
575
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
576
|
+
selectedAttrs.push(attributes[i].name);
|
|
627
577
|
}
|
|
578
|
+
} else {
|
|
579
|
+
selectedAttrs = res; // TBD: Check ensure is an array of strings
|
|
580
|
+
}
|
|
581
|
+
if (selectedAttrs.length === 0) {
|
|
582
|
+
// implies do nothing
|
|
583
|
+
logger.info(
|
|
584
|
+
context,
|
|
585
|
+
'sendUpdateValueNgsiLD \n none selectedAttrs with %j and ctxt %j',
|
|
586
|
+
typeInformation.explicitAttrs,
|
|
587
|
+
ctxt
|
|
588
|
+
);
|
|
589
|
+
return callback(null);
|
|
628
590
|
}
|
|
591
|
+
} catch (e) {
|
|
592
|
+
// nothing to do: exception is already logged at info level
|
|
593
|
+
}
|
|
629
594
|
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
595
|
+
typeInformation.active.forEach((attr) => {
|
|
596
|
+
if (selectedAttrs.includes(attr.name)) {
|
|
597
|
+
selectedAttrs.push(attr.object_id);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
} else if (typeInformation.explicitAttrs && typeof typeInformation.explicitAttrs === 'boolean') {
|
|
601
|
+
// TBD: selectedAttrs could be a boolean as a result of applyExpression
|
|
602
|
+
// explicitAtts is true => Add measures which are defined attributes
|
|
603
|
+
typeInformation.active.forEach((attr) => {
|
|
604
|
+
selectedAttrs.push(attr.name);
|
|
605
|
+
selectedAttrs.push(attr.object_id);
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
// This loop adds selected measured values (attributes) into payload entities (entity[0])
|
|
609
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
610
|
+
if (attributes[i].name && selectedAttrs.includes(attributes[i].name) && attributes[i].type) {
|
|
611
|
+
const attr = typeInformation.active.find((obj) => {
|
|
612
|
+
return obj.name === attributes[i].name;
|
|
613
|
+
});
|
|
614
|
+
payload[0][attributes[i].name] = {
|
|
615
|
+
value: attributes[i].value,
|
|
616
|
+
type: attributes[i].type
|
|
617
|
+
};
|
|
618
|
+
// ensure payload has attr with proper object_id
|
|
619
|
+
if (attr && attr.object_id) {
|
|
620
|
+
payload[0][attributes[i].name].object_id = attr.object_id;
|
|
621
|
+
}
|
|
622
|
+
const metadata = NGSIUtils.getMetaData(typeInformation, attributes[i].name, attributes[i].metadata);
|
|
623
|
+
if (metadata) {
|
|
624
|
+
payload[0][attributes[i].name].metadata = metadata;
|
|
625
|
+
}
|
|
626
|
+
} else if (attributes[i].name && !selectedAttrs.includes(attributes[i].name) && attributes[i].type) {
|
|
627
|
+
const att = {
|
|
628
|
+
name: attributes[i].name,
|
|
629
|
+
type: attributes[i].type,
|
|
630
|
+
value: attributes[i].value
|
|
631
|
+
};
|
|
632
|
+
measureAttrsForCtxt.push(att);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
logger.debug(
|
|
636
|
+
context,
|
|
637
|
+
'sendUpdateValueNgsiLD \n pre-initial explicitAttrs payload=%j \n selectedAttrs',
|
|
638
|
+
payload,
|
|
639
|
+
selectedAttrs
|
|
640
|
+
);
|
|
641
|
+
|
|
642
|
+
// Loop for add seleted attrs from type.information.active into pyaload entities (entity[0])
|
|
643
|
+
if (typeInformation.active) {
|
|
644
|
+
typeInformation.active.forEach((attr) => {
|
|
645
|
+
if (selectedAttrs.includes(attr.name)) {
|
|
646
|
+
if (attr.object_id) {
|
|
647
|
+
payload[0][attr.object_id] = {
|
|
648
|
+
value: payload[0][attr.object_id]
|
|
649
|
+
? payload[0][attr.object_id].value
|
|
650
|
+
: payload[0][attr.name]
|
|
651
|
+
? payload[0][attr.name].value
|
|
652
|
+
: undefined,
|
|
653
|
+
type: attr.type,
|
|
654
|
+
object_id: attr.object_id
|
|
655
|
+
};
|
|
633
656
|
} else {
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
657
|
+
payload[0][attr.name] = {
|
|
658
|
+
value: payload[0][attr.name] ? payload[0][attr.name].value : undefined,
|
|
659
|
+
type: attr.type
|
|
660
|
+
};
|
|
637
661
|
}
|
|
638
|
-
} catch (error) {
|
|
639
|
-
return callback(new errors.BadGeocoordinates(JSON.stringify(payload)));
|
|
640
662
|
}
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
} // END check explicitAttrs
|
|
666
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n initial payload=%j', payload);
|
|
667
|
+
|
|
668
|
+
const currentEntity = payload[0];
|
|
641
669
|
|
|
670
|
+
// Prepare attributes for expresionPlugin
|
|
671
|
+
const attsArray = pluginUtils.extractAttributesArrayFromNgsi2Entity(currentEntity);
|
|
672
|
+
|
|
673
|
+
// Exclude processing all attr expressions when current attr is of type 'commandStatus' or 'commandResult'
|
|
674
|
+
let attsArrayFiltered = [];
|
|
675
|
+
if (attsArray) {
|
|
676
|
+
attsArrayFiltered = attsArray.filter((obj) => {
|
|
677
|
+
return ![constants.COMMAND_STATUS, constants.COMMAND_RESULT].includes(obj.type);
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
let attributesCtxt = [...attsArrayFiltered]; // just copy
|
|
681
|
+
if (typeInformation.static) {
|
|
682
|
+
typeInformation.static.forEach(function (att) {
|
|
683
|
+
attributesCtxt.push(att);
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
if (measureAttrsForCtxt) {
|
|
687
|
+
measureAttrsForCtxt.forEach(function (att) {
|
|
688
|
+
attributesCtxt.push(att);
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
attributesCtxt = attributesCtxt.concat(idTypeSSSList);
|
|
692
|
+
const ctxt = expressionPlugin.extractContext(attributesCtxt, typeInformation);
|
|
693
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n initial ctxt %j ', ctxt);
|
|
694
|
+
|
|
695
|
+
// Sort currentEntity to get first attrs without expressions (checking attrs in typeInformation.active)
|
|
696
|
+
// attributes without expressions should be processed before
|
|
697
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n currentEntity %j ', currentEntity);
|
|
698
|
+
if (typeInformation.active && typeInformation.active.length > 0) {
|
|
699
|
+
for (const k in currentEntity) {
|
|
700
|
+
typeInformation.active.forEach(function (att) {
|
|
701
|
+
if (
|
|
702
|
+
(att.object_id && att.object_id === k && att.expression) ||
|
|
703
|
+
(att.name && att.name === k && att.expression)
|
|
704
|
+
) {
|
|
705
|
+
const m = currentEntity[k];
|
|
706
|
+
delete currentEntity[k];
|
|
707
|
+
currentEntity[k] = m; // put into the end of currentEntity
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n currentEntity sorted %j ', currentEntity);
|
|
713
|
+
let timestampValue;
|
|
714
|
+
// Loop for each final attribute to apply alias, multientity and expressions
|
|
715
|
+
for (const j in currentEntity) {
|
|
716
|
+
// discard id and type
|
|
717
|
+
if (j !== 'id' || j !== 'type') {
|
|
718
|
+
// Apply Mapping Alias: object_id in attributes are in typeInformation.active
|
|
719
|
+
let attr;
|
|
720
|
+
let newAttr = payload[0][j];
|
|
721
|
+
if (typeInformation.active) {
|
|
722
|
+
attr = typeInformation.active.find((obj) => {
|
|
723
|
+
return obj.object_id === j;
|
|
724
|
+
});
|
|
725
|
+
}
|
|
726
|
+
if (!attr) {
|
|
727
|
+
if (typeInformation.lazy) {
|
|
728
|
+
attr = typeInformation.lazy.find((obj) => {
|
|
729
|
+
return obj.object_id === j;
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
if (!attr) {
|
|
642
734
|
if (typeInformation.active) {
|
|
643
|
-
|
|
735
|
+
attr = typeInformation.active.find((obj) => {
|
|
736
|
+
return obj.name === j;
|
|
737
|
+
});
|
|
644
738
|
}
|
|
739
|
+
}
|
|
740
|
+
if (attr && attr.name) {
|
|
741
|
+
if (['id', 'type'].includes(attr.name)) {
|
|
742
|
+
// invalid mapping
|
|
743
|
+
logger.debug(
|
|
744
|
+
context,
|
|
745
|
+
'sendUpdateValueNgsiLD \n invalid mapping for attr %j \n newAttr %j',
|
|
746
|
+
attr,
|
|
747
|
+
newAttr
|
|
748
|
+
);
|
|
749
|
+
delete payload[0][attr.object_id];
|
|
750
|
+
attr = undefined; // stop processing attr
|
|
751
|
+
newAttr = undefined;
|
|
752
|
+
} else {
|
|
753
|
+
ctxt[attr.name] = payload[0][j].value;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
logger.debug(
|
|
757
|
+
context,
|
|
758
|
+
'sendUpdateValueNgsiLD \n procesing j %j attr %j ctxt %j \n newAttr %j ',
|
|
759
|
+
j,
|
|
760
|
+
attr,
|
|
761
|
+
ctxt,
|
|
762
|
+
newAttr
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
if (attr && attr.type) {
|
|
766
|
+
if (attr.type !== 'GeoProperty') {
|
|
767
|
+
newAttr.type = attr.type;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
645
770
|
|
|
646
|
-
|
|
771
|
+
// Apply expression
|
|
772
|
+
if (attr && attr.expression) {
|
|
647
773
|
logger.debug(
|
|
648
774
|
context,
|
|
649
|
-
'
|
|
650
|
-
|
|
775
|
+
'sendUpdateValueNgsiLD \n apply expression %j \n over ctxt %j \n and device %j',
|
|
776
|
+
attr.expression,
|
|
777
|
+
ctxt,
|
|
778
|
+
typeInformation
|
|
651
779
|
);
|
|
780
|
+
let res = null;
|
|
781
|
+
try {
|
|
782
|
+
if (expressionPlugin.contextAvailable(attr.expression, ctxt, typeInformation)) {
|
|
783
|
+
res = expressionPlugin.applyExpression(attr.expression, ctxt, typeInformation);
|
|
784
|
+
} else {
|
|
785
|
+
logger.warn(
|
|
786
|
+
context,
|
|
787
|
+
'sendUpdateValueNgsiLD \n no context available for apply expression %j \n',
|
|
788
|
+
attr.expression
|
|
789
|
+
);
|
|
790
|
+
res = newAttr.value; // keep newAttr value
|
|
791
|
+
}
|
|
792
|
+
} catch (e) {
|
|
793
|
+
logger.error(context, 'sendUpdateValueNgsiLD \n apply expression exception %j \n', e);
|
|
794
|
+
if (!expressionPlugin.checkJexl(typeInformation)) {
|
|
795
|
+
return callback(e); // just throw error with legacy parser for backward compatiblity
|
|
796
|
+
} else {
|
|
797
|
+
res = ctxt[attr.name]; // TBD: add reference to test
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (!expressionPlugin.checkJexl(typeInformation)) {
|
|
801
|
+
// legacy expression plugin: Involves some legacy checks performed by applierExpression
|
|
802
|
+
if (
|
|
803
|
+
res &&
|
|
804
|
+
res !== 'undefined' &&
|
|
805
|
+
res !== 'null' &&
|
|
806
|
+
(typeof res === 'string' || res instanceof String) &&
|
|
807
|
+
!res.includes('NaN')
|
|
808
|
+
) {
|
|
809
|
+
newAttr.value = res;
|
|
810
|
+
if (newAttr.type === 'Number' && isFloat(newAttr.value)) {
|
|
811
|
+
newAttr.value = Number.parseFloat(newAttr.value);
|
|
812
|
+
} else if (newAttr.type === 'Number' && !Number.isNaN(Number.parseInt(newAttr.value))) {
|
|
813
|
+
newAttr.value = Number.parseInt(newAttr.value);
|
|
814
|
+
} else if (newAttr.type === 'Boolean') {
|
|
815
|
+
newAttr.value = newAttr.value === 'true' || newAttr.value === '1';
|
|
816
|
+
} else if (newAttr.type === 'None') {
|
|
817
|
+
newAttr.value = null;
|
|
818
|
+
}
|
|
819
|
+
} else if (isNaN(res) || res === 'undefined' || res === 'null') {
|
|
820
|
+
logger.debug(
|
|
821
|
+
context,
|
|
822
|
+
'sendUpdateValueNgsiLD \n apply expression result: isNaN || undefined || null'
|
|
823
|
+
);
|
|
824
|
+
if (res === 'null' && newAttr.type === 'None') {
|
|
825
|
+
newAttr.value = null;
|
|
826
|
+
} else {
|
|
827
|
+
delete payload[0][j]; // remove measure attr
|
|
828
|
+
attr = undefined; // stop process attr
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
} else {
|
|
832
|
+
// jexl expression plugin
|
|
833
|
+
newAttr.value = res;
|
|
834
|
+
}
|
|
835
|
+
logger.debug(
|
|
836
|
+
context,
|
|
837
|
+
'sendUpdateValueNgsiLD \n apply expression result %j \n newAttr %j',
|
|
838
|
+
res,
|
|
839
|
+
newAttr
|
|
840
|
+
);
|
|
841
|
+
}
|
|
652
842
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
843
|
+
// Apply Multientity: entity_type and entity_name in attributes are in typeInformation.active
|
|
844
|
+
if (attr && (attr.entity_type || attr.entity_name)) {
|
|
845
|
+
// Create a newEntity for this attribute
|
|
846
|
+
let newEntityName = null;
|
|
847
|
+
if (attr.entity_name) {
|
|
848
|
+
try {
|
|
849
|
+
if (expressionPlugin.contextAvailable(attr.entity_name, ctxt, typeInformation)) {
|
|
850
|
+
newEntityName = expressionPlugin.applyExpression(attr.entity_name, ctxt, typeInformation);
|
|
851
|
+
} else {
|
|
852
|
+
logger.warn(
|
|
853
|
+
context,
|
|
854
|
+
'sendUpdateValueNgsiLD \n MULTI no context available for apply expression %j \n',
|
|
855
|
+
attr.entity_name
|
|
856
|
+
);
|
|
857
|
+
newEntityName = attr.entity_name;
|
|
858
|
+
}
|
|
859
|
+
newEntityName = newEntityName ? newEntityName : attr.entity_name;
|
|
860
|
+
} catch (e) {
|
|
861
|
+
logger.error(context, 'sendUpdateValueNgsiLD \n MULTI apply expression exception %j \n', e);
|
|
862
|
+
newEntityName = attr.entity_name;
|
|
863
|
+
}
|
|
864
|
+
logger.debug(
|
|
865
|
+
context,
|
|
866
|
+
'sendUpdateValueNgsiLD \n MULTI apply expression %j \n result %j \n payload %j',
|
|
867
|
+
attr.entity_name,
|
|
868
|
+
newEntityName,
|
|
869
|
+
payload
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
let newEntity = {
|
|
874
|
+
id: newEntityName ? newEntityName : payload[0].id,
|
|
875
|
+
type: attr.entity_type ? attr.entity_type : payload[0].type
|
|
876
|
+
};
|
|
877
|
+
// Check if there is already a newEntity created
|
|
878
|
+
const alreadyEntity = payload.find((entity) => {
|
|
879
|
+
return entity.id === newEntity.id && entity.type === newEntity.type;
|
|
880
|
+
});
|
|
881
|
+
if (alreadyEntity) {
|
|
882
|
+
// Use alreadyEntity
|
|
883
|
+
alreadyEntity[attr.name] = newAttr;
|
|
884
|
+
} else {
|
|
885
|
+
// Add newEntity to payload
|
|
886
|
+
newEntity[attr.name] = newAttr;
|
|
887
|
+
if (
|
|
888
|
+
'timestamp' in typeInformation && typeInformation.timestamp !== undefined
|
|
889
|
+
? typeInformation.timestamp
|
|
890
|
+
: config.getConfig().timestamp !== undefined
|
|
891
|
+
? config.getConfig().timestamp
|
|
892
|
+
: timestampValue !== undefined
|
|
893
|
+
) {
|
|
894
|
+
newEntity = NGSIv2.addTimestamp(newEntity, typeInformation.timezone, timestampValue);
|
|
895
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n timestamped newEntity=%j', newEntity);
|
|
896
|
+
}
|
|
897
|
+
payload.push(newEntity);
|
|
898
|
+
}
|
|
899
|
+
if (attr && attr.name) {
|
|
900
|
+
if (attr.name !== j) {
|
|
901
|
+
logger.debug(
|
|
902
|
+
context,
|
|
903
|
+
'sendUpdateValueNgsiLD \n MULTI remove measure attr %j keep alias j %j from %j \n',
|
|
904
|
+
j,
|
|
905
|
+
attr,
|
|
906
|
+
payload
|
|
907
|
+
);
|
|
908
|
+
delete payload[0][j];
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
// if (attr && (attr.entity_type || attr.entity_name))
|
|
912
|
+
} else {
|
|
913
|
+
// Not a multientity attr
|
|
914
|
+
if (attr && attr.name) {
|
|
915
|
+
payload[0][attr.name] = newAttr;
|
|
916
|
+
if (attr.name !== j) {
|
|
917
|
+
delete payload[0][j]; // keep alias name, remove measure name
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (newAttr && newAttr.type === constants.TIMESTAMP_TYPE_NGSI2 && newAttr.value) {
|
|
921
|
+
const extendedTime = compressTimestampPlugin.fromBasicToExtended(newAttr.value);
|
|
922
|
+
if (extendedTime) {
|
|
923
|
+
// TBD: there is not flag about compressTimestamp in iotagent-node-lib,
|
|
924
|
+
// but there is one in agents
|
|
925
|
+
newAttr.value = extendedTime;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
if (j === constants.TIMESTAMP_ATTRIBUTE) {
|
|
929
|
+
if (newAttr && newAttr.type === constants.TIMESTAMP_TYPE_NGSI2 && newAttr.value) {
|
|
930
|
+
timestampValue = newAttr.value;
|
|
931
|
+
logger.debug(
|
|
932
|
+
context,
|
|
933
|
+
'sendUpdateValueNgsiLD \n newAttr is TimeInstant and new payload=%j',
|
|
934
|
+
payload
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
if (
|
|
939
|
+
newAttr &&
|
|
940
|
+
newAttr.metadata &&
|
|
941
|
+
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE] &&
|
|
942
|
+
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].type === constants.TIMESTAMP_TYPE_NGSI2 &&
|
|
943
|
+
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].value
|
|
944
|
+
) {
|
|
945
|
+
const extendedTime = compressTimestampPlugin.fromBasicToExtended(
|
|
946
|
+
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].value
|
|
947
|
+
);
|
|
948
|
+
if (extendedTime) {
|
|
949
|
+
newAttr.metadata[constants.TIMESTAMP_ATTRIBUTE].value = extendedTime;
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
} // if (j !== 'id' || j !== 'type')
|
|
954
|
+
|
|
955
|
+
// final attr loop
|
|
956
|
+
logger.debug(
|
|
957
|
+
context,
|
|
958
|
+
'sendUpdateValueNgsiLD \n after procesing attr %j \n current entity %j \n current payload=%j',
|
|
959
|
+
j,
|
|
960
|
+
currentEntity,
|
|
961
|
+
payload
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
// for attr loop
|
|
965
|
+
|
|
966
|
+
// Add timestamp to paylaod
|
|
967
|
+
if (
|
|
968
|
+
'timestamp' in typeInformation && typeInformation.timestamp !== undefined
|
|
969
|
+
? typeInformation.timestamp
|
|
970
|
+
: config.getConfig().timestamp !== undefined
|
|
971
|
+
? config.getConfig().timestamp
|
|
972
|
+
: timestampValue !== undefined
|
|
973
|
+
) {
|
|
974
|
+
if (timestampValue) {
|
|
975
|
+
// timeInstant is provided as measure
|
|
976
|
+
if (Object.keys(payload[0]).length > 3) {
|
|
977
|
+
// include metadata with TimeInstant in attrs when TimeInstant is provided as measure in all entities
|
|
978
|
+
payload[0] = NGSIv2.addTimestamp(
|
|
979
|
+
payload[0],
|
|
980
|
+
typeInformation.timezone,
|
|
981
|
+
timestampValue,
|
|
982
|
+
false // skipMetadataAtt
|
|
983
|
+
);
|
|
984
|
+
} else {
|
|
985
|
+
// Do not include metadata with TimeInstant in attrs when TimeInstant is provided as measure
|
|
986
|
+
// and no more entities
|
|
987
|
+
payload[0] = NGSIv2.addTimestamp(
|
|
988
|
+
payload[0],
|
|
989
|
+
typeInformation.timezone,
|
|
990
|
+
timestampValue,
|
|
991
|
+
true // skipMetadataAtt
|
|
656
992
|
);
|
|
657
993
|
}
|
|
994
|
+
} else {
|
|
995
|
+
// jshint maxdepth:5
|
|
996
|
+
for (let n = 0; n < payload.length; n++) {
|
|
997
|
+
if (!utils.isTimestampedNgsi2(payload[n])) {
|
|
998
|
+
// legacy check needed?
|
|
999
|
+
payload[n] = NGSIv2.addTimestamp(payload[n], typeInformation.timezone);
|
|
1000
|
+
// jshint maxdepth:5
|
|
1001
|
+
} else if (!utils.IsValidTimestampedNgsi2(payload[n])) {
|
|
1002
|
+
// legacy check needed?
|
|
1003
|
+
logger.error(context, 'Invalid timestamp:%s', JSON.stringify(payload[0]));
|
|
1004
|
+
callback(new errors.BadTimestamp(payload));
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
658
1008
|
}
|
|
659
|
-
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n ending payload=%j', payload);
|
|
1012
|
+
|
|
1013
|
+
for (let m = 0; m < payload.length; m++) {
|
|
1014
|
+
for (const key in payload[m]) {
|
|
1015
|
+
// purge object_id from payload
|
|
1016
|
+
if (payload[m][key] && payload[m][key].object_id) {
|
|
1017
|
+
delete payload[m][key].object_id;
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
payload[m] = NGSIUtils.castJsonNativeAttributes(payload[m]); // native types
|
|
1021
|
+
}
|
|
1022
|
+
logger.debug(context, 'sendUpdateValueNgsiLD \n payload with native types and without object_id %j', payload);
|
|
1023
|
+
|
|
1024
|
+
options.json = payload;
|
|
1025
|
+
try {
|
|
1026
|
+
if (payload instanceof Array) {
|
|
1027
|
+
options.json = _.map(options.json, formatAsNGSILD);
|
|
1028
|
+
} else {
|
|
1029
|
+
options.json.id = entityName;
|
|
1030
|
+
options.json.type = typeInformation.type;
|
|
1031
|
+
options.json = [formatAsNGSILD(options.json)];
|
|
1032
|
+
}
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
return callback(new errors.BadGeocoordinates(JSON.stringify(payload)));
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
if (typeInformation.active) {
|
|
1038
|
+
addLinkedEntities(typeInformation, options.json);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// Prevent to update an entity with an empty payload
|
|
1042
|
+
if (
|
|
1043
|
+
Object.keys(options.json).length > 0 &&
|
|
1044
|
+
(options.json.length > 1 || (options.json.length === 1 && Object.keys(options.json[0]).length > 2)) // more than id and type
|
|
1045
|
+
) {
|
|
1046
|
+
logger.debug(context, 'Updating device value in the Context Broker at [%s]', options.url);
|
|
1047
|
+
logger.debug(context, 'Using the following NGSI LD request:\n\n%s\n\n', JSON.stringify(options, null, 4));
|
|
1048
|
+
request(
|
|
1049
|
+
options,
|
|
1050
|
+
generateNGSILDOperationHandler('update', entityName, typeInformation, token, options, callback)
|
|
1051
|
+
);
|
|
1052
|
+
} else {
|
|
1053
|
+
logger.debug(
|
|
1054
|
+
context,
|
|
1055
|
+
'Not updating device value in the Context Broker at [%s] due to empty payload \n\n[%s]\n\n',
|
|
1056
|
+
options.url,
|
|
1057
|
+
JSON.stringify(options, null, 4)
|
|
1058
|
+
);
|
|
1059
|
+
callback(null);
|
|
1060
|
+
}
|
|
660
1061
|
}
|
|
661
1062
|
|
|
662
1063
|
exports.convertAttrNGSILD = convertAttrNGSILD;
|