Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix timestamp mapped attribute cases #1593

Merged
merged 21 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- Fix timestamp attribute mapped cases (#1557)
- Fix: reduce information showed handling errors to just config flags (#1594)
- Upgrade express dep from 4.18.1 to 4.19.2
- Add: allow devices with the same device_id in the same service and subservice but different apikey (#1589)

4 changes: 2 additions & 2 deletions doc/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ parameters defined at device level in database, the parameters are inherit from

Group service uniqueness is defined by the combination of: service, subservice and apikey

Device uniqueness is defined by the combination of: service, subservice, device_id and apikey. Note that several
devices with the same device_id are allowed in the same service and subservice as long as their apikeys are different.
Device uniqueness is defined by the combination of: service, subservice, device_id and apikey. Note that several devices
with the same device_id are allowed in the same service and subservice as long as their apikeys are different.

## Special measures and attributes names

Expand Down
87 changes: 37 additions & 50 deletions lib/services/ngsi/entities-NGSI-v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,6 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
let entities = {}; //{entityName:{entityType:[attrs]}} //SubGoal Populate entoties data striucture
let jexlctxt = {}; //will store the whole context (not just for JEXL)
let payload = {}; //will store the final payload
let timestamp = { type: constants.TIMESTAMP_TYPE_NGSI2 }; //timestamp scafold-attr for insertions.
let plainMeasures = null; //will contain measures POJO
let idTypeSSSList = pluginUtils.getIdTypeServSubServiceFromDevice(typeInformation);

Expand Down Expand Up @@ -308,43 +307,16 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
jexlctxt = reduceAttrToPlainObject(idTypeSSSList, jexlctxt);

//Managing timestamp (mustInsertTimeInstant flag to decide if we should insert Timestamp later on)
const mustInsertTimeInstant =
typeInformation.timestamp !== undefined
? typeInformation.timestamp
: false;

if (mustInsertTimeInstant) {
//remove TimeInstant from measures
measures = measures.filter((item) => item.name !== constants.TIMESTAMP_ATTRIBUTE);

if (plainMeasures[constants.TIMESTAMP_ATTRIBUTE]) {
//if it comes from a measure
if (moment(plainMeasures[constants.TIMESTAMP_ATTRIBUTE], moment.ISO_8601, true).isValid()) {
timestamp.value = plainMeasures[constants.TIMESTAMP_ATTRIBUTE];
} else {
callback(
new errors.BadTimestamp(plainMeasures[constants.TIMESTAMP_ATTRIBUTE], entityName, typeInformation)
);
return;
}
} else if (!typeInformation.timezone) {
timestamp.value = new Date().toISOString();
jexlctxt[constants.TIMESTAMP_ATTRIBUTE] = timestamp.value;
} else {
timestamp.value = moment().tz(typeInformation.timezone).format('YYYY-MM-DD[T]HH:mm:ss.SSSZ');
jexlctxt[constants.TIMESTAMP_ATTRIBUTE] = timestamp.value;
}
}
const mustInsertTimeInstant = typeInformation.timestamp !== undefined ? typeInformation.timestamp : false;

logger.debug(
context,
'sendUpdateValueNgsi2 called with: entityName=%s, measures=%j, typeInformation=%j, initial jexlContext=%j, timestamp=%j with value=%j',
'sendUpdateValueNgsi2 called with: entityName=%s, measures=%j, typeInformation=%j, initial jexlContext=%j, timestamp=%j',
entityName,
plainMeasures,
typeInformation,
jexlctxt,
mustInsertTimeInstant,
timestamp.value
mustInsertTimeInstant
);

//Now we can calculate the EntityName of primary entity
Expand Down Expand Up @@ -478,15 +450,6 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call

currentAttr.hitted = hitted;
currentAttr.value = valueExpression;

//add TimeInstant to attr metadata
if (mustInsertTimeInstant) {
if (!currentAttr.metadata) {
currentAttr.metadata = {};
}
currentAttr.metadata[constants.TIMESTAMP_ATTRIBUTE] = timestamp;
}

//store de New Attributte in entity data structure
if (hitted === true) {
if (entities[attrEntityName] === undefined) {
Expand Down Expand Up @@ -527,16 +490,6 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call

//more mesures may be added to the attribute list (unnhandled/left mesaures) l
if (explicit === false && Object.keys(measures).length > 0) {
//add Timestamp to measures if needed
if (mustInsertTimeInstant) {
for (let currentMeasure of measures) {
if (!currentMeasure.metadata) {
currentMeasure.metadata = {};
}
currentMeasure.metadata[constants.TIMESTAMP_ATTRIBUTE] = timestamp;
}
//If just measures in the principal entity we missed the Timestamp.
}
entities[entityName][typeInformation.type] = entities[entityName][typeInformation.type].concat(measures);
}

Expand All @@ -552,6 +505,31 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
let e = {};
e.id = String(ename);
e.type = String(etype);
let timestamp = { type: constants.TIMESTAMP_TYPE_NGSI2 }; //timestamp scafold-attr for insertions.
let timestampAttrs = null;
if (mustInsertTimeInstant) {
// get timestamp for current entity

timestampAttrs = entities[ename][etype].filter((item) => item.name === constants.TIMESTAMP_ATTRIBUTE);
if (timestampAttrs && timestampAttrs.length > 0) {
timestamp.value = timestampAttrs[0]['value'];
}

if (timestamp.value) {
if (!moment(timestamp.value, moment.ISO_8601, true).isValid()) {
callback(new errors.BadTimestamp(timestamp.value, entityName, typeInformation));
return;
}
} else {
if (!typeInformation.timezone) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this could be precalculated to simplify logic and optimize. Minor.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 4c8a624

timestamp.value = new Date().toISOString();
jexlctxt[constants.TIMESTAMP_ATTRIBUTE] = timestamp.value; // nosense
} else {
timestamp.value = moment().tz(typeInformation.timezone).format('YYYY-MM-DD[T]HH:mm:ss.SSSZ');
jexlctxt[constants.TIMESTAMP_ATTRIBUTE] = timestamp.value; // nosense
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jexlctxt is not used any more from this point

}
}
}
//extract attributes
let isEmpty = true;
for (let attr of entities[ename][etype]) {
Expand All @@ -568,6 +546,15 @@ function sendUpdateValueNgsi2(entityName, measures, typeInformation, token, call
))))
) {
isEmpty = false;
if (mustInsertTimeInstant) {
// Add TimeInstant to all attribute metadata of all entities
if (attr.name !== constants.TIMESTAMP_ATTRIBUTE) {
if (!attr.metadata) {
attr.metadata = {};
}
attr.metadata[constants.TIMESTAMP_ATTRIBUTE] = timestamp;
}
}
e[attr.name] = { type: attr.type, value: attr.value, metadata: attr.metadata };
}
}
Expand Down
Loading
Loading