Skip to content

Commit

Permalink
Merge pull request #4600 from telefonicaid/feature/4594_metadata_in_j…
Browse files Browse the repository at this point in the history
…exl_context

ADD metadata in JEXL context
  • Loading branch information
mapedraza authored Aug 2, 2024
2 parents 5633157 + 2256824 commit 36adc96
Show file tree
Hide file tree
Showing 8 changed files with 863 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
- Add: custom notification ngsi patching evaluation priority based in evalPriority builtin metadata (#4556)
- Add: attribute metadata to JEXL context (#4594)
- Fix: $max and $min operators were not supported with DateTime attributes (#4585)
- Fix: wrong date values should not allowed in subscription's expires field (#4541)
- Fix: do not raise DB alarm in case of wrong GeoJSON in client request
Expand Down
14 changes: 14 additions & 0 deletions doc/manuals/orion-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
- [Additional considerations](#additional-considerations)
- [JEXL Support](#jexl-support)
- [JEXL usage example](#jexl-usage-example)
- [Metadata support](#metadata-support)
- [Evaluation priority](#evaluation-priority)
- [Available Transformations](#available-transformations)
- [`uppercase`](#uppercase)
Expand Down Expand Up @@ -2514,6 +2515,19 @@ will trigger a notification like this:
]
```

### Metadata support

Attribute metadata is also included in the JEXL evaluation context, along with the actual attributes. This is done using a special context key named `metadata` which value is an object with the following structure:

* keys are the name of the attributes
* values are object with a key-map of the metadata values of the given attribute

For instance, the expression `${metadata.A.MD1}` will result in the value of the metadata `MD1` belonging to the attribute `A`.

Note that [builtin metadata](#builtin-metadata) are automatically added to the `metadata` context key. For instance, the `${A-metadata.A.previousValue}` expression will provide the difference between the current value of `A` (in the update request) and the previous one (before the update request).

Finally, note that in the rare case an attribute named `metadata` is used (which is an anti-pattern strongly discouraged) it will not be added to the context, as the `metadata` with metadata values will take precedence.

### Evaluation priority

Each time an expression is evaluated, it is added to the expression context, so it can be reused by other expressions. However, by default Orion does not guarantee a given evaluation other.
Expand Down
68 changes: 68 additions & 0 deletions src/lib/ngsi/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -666,3 +666,71 @@ void Metadata::appendToBsoN(orion::BSONObjBuilder* mdBuilder, orion::BSONArrayBu
}
}
}


/* ****************************************************************************
*
* addToContext -
*
* FIXME P5: duplicated from ContextAttrMetibute::addToContext(). Maybe they should be unified
*/
void Metadata::addToContext(ExprContextObject* exprContextObjectP, bool legacy)
{
if (compoundValueP != NULL)
{
// In legacy expression, objects are vector are strings to be stored in a std::map<std::string,std::string>
if (valueType == orion::ValueTypeObject)
{
if (legacy)
{
exprContextObjectP->add(name, compoundValueP->toJson(), true);
}
else
{
exprContextObjectP->add(name, compoundValueP->toExprContextObject());
}
}
else // valueType == orion::ValueTypeVector
{
if (legacy)
{
exprContextObjectP->add(name, compoundValueP->toJson(), true);
}
else
{
exprContextObjectP->add(name, compoundValueP->toExprContextList());
}
}
}
else if (valueType == orion::ValueTypeNumber)
{
if ((type == DATE_TYPE) || (type == DATE_TYPE_ALT))
{
exprContextObjectP->add(name, toJsonString(isodate2str(numberValue)));
}
else // regular number
{
exprContextObjectP->add(name, numberValue);
}
}
else if (valueType == orion::ValueTypeString)
{
exprContextObjectP->add(name, toJsonString(stringValue));
}
else if (valueType == orion::ValueTypeBoolean)
{
exprContextObjectP->add(name, boolValue);
}
else if (valueType == orion::ValueTypeNull)
{
exprContextObjectP->add(name);
}
else if (valueType == orion::ValueTypeNotGiven)
{
LM_E(("Runtime Error (value not given for metadata %s)", name.c_str()));
}
else
{
LM_E(("Runtime Error (invalid value type %s for metadata %s)", valueTypeName(valueType), name.c_str()));
}
}
2 changes: 2 additions & 0 deletions src/lib/ngsi/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ typedef struct Metadata

void appendToBsoN(orion::BSONObjBuilder* md, orion::BSONArrayBuilder* mdNames, bool useDefaultType);

void addToContext(ExprContextObject* exprContextObjectP, bool legacy);

std::string check(ApiVersion apiVersion);
} Metadata;

Expand Down
16 changes: 16 additions & 0 deletions src/lib/ngsiNotify/Notifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ static SenderThreadParams* buildSenderParamsCustom

// Used by several macroSubstitute() calls along this function
ExprContextObject exprContext(basic);
ExprContextObject exprMetadataContext(basic);

// It seems that add() semantics are different in basic and jexl mode. In jexl mode, if the key already exists, it is
// updated (in other words, the last added keys is the one that takes precedence). In basic model, if the key already
Expand All @@ -376,16 +377,31 @@ static SenderThreadParams* buildSenderParamsCustom
TIME_EXPR_CTXBLD_START();
exprContext.add("id", en.id);
exprContext.add("type", en.type);

if (!basic)
{
exprContext.add("service", tenant);
exprContext.add("servicePath", en.servicePath);
exprContext.add("authToken", xauthToken);
}

for (unsigned int ix = 0; ix < en.attributeVector.size(); ix++)
{
en.attributeVector[ix]->addToContext(&exprContext, basic);

// Add attribute metadata to context
ExprContextObject exprAttrMetadataContext(basic);
for (unsigned int jx = 0; jx < en.attributeVector[ix]->metadataVector.size(); jx++)
{
en.attributeVector[ix]->metadataVector[jx]->addToContext(&exprAttrMetadataContext, basic);
}
exprMetadataContext.add(en.attributeVector[ix]->name, exprAttrMetadataContext);
}

// Add all metadata under the "metadata" context key
// (note that in JEXL if the key already exists, it is updated, so attribute with name "metadata" will never be appear in context)
exprContext.add("metadata", exprMetadataContext);

if (basic)
{
exprContext.add("service", tenant);
Expand Down
Loading

0 comments on commit 36adc96

Please sign in to comment.