Skip to content

Commit bd3b361

Browse files
authored
Merge pull request #4738 from telefonicaid/feature/4555_jexlexpression_in_subject_condition
JEXL expression in subject.condition #4555
2 parents 1522508 + 42bc56a commit bd3b361

30 files changed

+2439
-15
lines changed

CHANGES_NEXT_RELEASE

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
- Fix: kafkaCustom.headers support including the ability to override the default Fiware-Service and Fiware-ServicePath headers (#4726)
1+
- Fix: kafkaCustom.headers support including the ability to override the default Fiware-Service and Fiware-ServicePath headers (#4726)
2+
- Add: `jexlExpression` field to subscription conditions (`subject.condition`) (#4555)

doc/manuals/admin/database_model.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,8 @@ Fields:
302302
- **expression**: an expression used to evaluate if notifications has
303303
to be sent or not when updates come. It may be composed of the following
304304
fields: q, mq, georel, geometry and/or coords (optional)
305+
- **jexlExpression**: an expression used to determine whether a notification must be sent.
306+
Notifications are triggered only when the expression evaluates to `true` if this field is used (it is optional).
305307
- **count**: the number of notifications sent associated to
306308
the subscription.
307309
- **format**: the format to use to send notification, possible values are **normalized**, **keyValues**, **simplifiedNormalized**, **simplifiedKeyValues** and **values**.
@@ -376,6 +378,7 @@ Example document:
376378
"coords" : "",
377379
"georel" : ""
378380
},
381+
"jexlExpression": "temperature > 40"
379382
"format" : "JSON",
380383
"status" : "active",
381384
"statusLastChange" : 1637226173.6940024

doc/manuals/admin/statistics.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ The particular counters are as follows:
165165
`last` includes the accumulation for all of them. In the case of mongoReadWait, only the time used
166166
to get the results cursor is taken into account, but not the time to process cursors results (which
167167
is time that belongs to mongoBackend counters).
168-
* `exprJexlCtxBld`: time passed building context for custom notification expression evaluation (see [macro substitution](../orion-api.md#macro-substitution) and [JEXL support](../orion-api.md#jexl-support))
169-
* `exprJexlEval`: time passed evaluating custom notification expressions (see [macro substitution](../orion-api.md#macro-substitution) and [JEXL support](../orion-api.md#jexl-support))
168+
* `exprJexlCtxBld`: time passed building context for custom notification expression evaluation (see [macro substitution](../orion-api.md#macro-substitution) and [JEXL support in custom notifications](../orion-api.md#jexl-support-in-custom-notifications)). This also includes the time passed building context for `[`jexlExpressions`](../orion-api.md#subscriptionsubjectcondition).
169+
* `exprJexlEval`: time passed evaluating custom notification expressions (see [macro substitution](../orion-api.md#macro-substitution) and [JEXL support in custom notifications](../orion-api.md#jexl-support-in-custom-notifications)). This also includes the time passed evaluating expressions for `[`jexlExpressions`](../orion-api.md#subscriptionsubjectcondition).
170170

171171
*NOTE*: if Orion binary is build without using cjexl and only basic replacement is available, then `exprBasicCtxtBld` and `exprBasicEval`
172172
fields appear instead of `exprJexlCtxBld` and `exprJexlEval`.

doc/manuals/orion-api.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
- [NGSI payload patching](#ngsi-payload-patching)
7474
- [Omitting payload](#omitting-payload)
7575
- [Additional considerations](#additional-considerations)
76-
- [JEXL Support](#jexl-support)
76+
- [JEXL support in custom notifications](#jexl-support-in-custom-notifications)
7777
- [JEXL usage example](#jexl-usage-example)
7878
- [Metadata support](#metadata-support)
7979
- [Evaluation priority](#evaluation-priority)
@@ -452,7 +452,7 @@ There are some exception cases in which the above restrictions do not apply. In
452452
* URL parameter `q` allows the special characters needed by the [Simple Query Language](#simple-query-language)
453453
* URL parameter `mq` allows the special characters needed by the [Simple Query Language](#simple-query-language)
454454
* URL parameter `georel` and `coords` allow `;`
455-
* Within `ngsi` (i.e. `id`, `type` and attribute values) in [NGSI Payload patching](#ngsi-payload-patching) (to support characters used in the [JEXL expression syntax](#jexl-support))
455+
* Within `ngsi` (i.e. `id`, `type` and attribute values) in [NGSI Payload patching](#ngsi-payload-patching) (to support characters used in the [JEXL support in custom notifications](#jexl-support-in-custom-notifications))
456456
* Whichever attribute value which uses `TextUnrestricted` as attribute type (see [Special Attribute Types](#special-attribute-types) section)
457457

458458
## Identifiers syntax restrictions
@@ -478,7 +478,7 @@ In addition, the [General syntax restrictions](#general-syntax-restrictions) als
478478

479479
In case a client attempts to use a field that is invalid from a syntax point of view, the client gets a "Bad Request" error response, explaining the cause.
480480

481-
Note that although `:` and `-` are allowed in identifiers, they are strongly discouraged, as they collide with the [JEXL syntax](#jexl-support). In particular, `-` is used for subtraction operation (e.g. `${A-B}`) and `:` is used in the ternary operator (eg. `A?'A is true':'A is false`). Thus, an attribute name `lower-temperature` in an expression `${lower-temperature}` would be interpreted as the value of `lower` attribute minus `temperature` attribute (and not as the value of an attribute named `lower-temperature`).
481+
Note that although `:` and `-` are allowed in identifiers, they are strongly discouraged, as they collide with the [JEXL syntax](#jexl-support-in-custom-notifications). In particular, `-` is used for subtraction operation (e.g. `${A-B}`) and `:` is used in the ternary operator (eg. `A?'A is true':'A is false`). Thus, an attribute name `lower-temperature` in an expression `${lower-temperature}` would be interpreted as the value of `lower` attribute minus `temperature` attribute (and not as the value of an attribute named `lower-temperature`).
482482

483483
## Error Responses
484484

@@ -1930,18 +1930,25 @@ be automatically deleted in an unwanted way**.
19301930
Based on the [`condition` subscription field](#subscriptionsubjectcondition), upon
19311931
entity update, the notification triggering rules are as follow:
19321932

1933-
* If `attrs` and `expression` are used, a notification is sent whenever one of the attributes in
1934-
the `attrs` list changes (or is deleted) and at the same time `expression` matches.
1935-
* If `attrs` is used and `expression` is not used, a notification is sent whenever any of the
1933+
* If `attrs` and expressions are used, a notification is sent whenever one of the attributes in
1934+
the `attrs` list changes (or is deleted) and at the same time expressions match.
1935+
* If `attrs` is used and expressions are not used, a notification is sent whenever any of the
19361936
attributes in the `attrs` list changes (or is deleted).
1937-
* If `attrs` is not used and `expression` is used, a notification is sent whenever any of the
1938-
attributes of the entity changes (or is deleted) and at the same time `expression` matches.
1939-
* If neither `attrs` nor `expression` are used, a notification is sent whenever any of the
1937+
* If `attrs` is not used and expressions are used, a notification is sent whenever any of the
1938+
attributes of the entity change (or is deleted) and at the same time expressions match.
1939+
* If neither `attrs` nor expressions are used, a notification is sent whenever any of the
19401940
attributes of the entity changes (or is deleted).
19411941

19421942
Note that changing the metadata of a given attribute is considered a change even though the attribute
19431943
value itself hasn't changed.
19441944

1945+
By *expressions* we mean at least one of the following
1946+
1947+
* `expression`
1948+
* `jexlExpression`
1949+
1950+
If both are used (i.e. `expression` and a `jexlExpresion` at the same time) both must match to consider that expressions match in the above description of triggering rules.
1951+
19451952
## Notification Messages
19461953

19471954
Notifications include two fields:
@@ -2069,7 +2076,7 @@ In case of `mqttCustom`:
20692076
* `topic`
20702077

20712078
Macro substitution for templates is based on the syntax `${<JEXL expression>}`. The support to JEXL
2072-
is explained in [JEXL Support](#jexl-support) section. The following identifiers are included in
2079+
is explained in [JEXL support in custom notifications](#jexl-support-in-custom-notifications) section. The following identifiers are included in
20732080
the context evaluated by the JEXL expression:
20742081

20752082
* `id`: for the `id` of the entity
@@ -2341,7 +2348,7 @@ Some considerations to take into account when using custom notifications:
23412348
(i.e. `ngsi` field) then `Ngsiv2-AttrsFormat: normalized` is used, as in a regular
23422349
notification (given that the notification format is actually the same).
23432350

2344-
## JEXL Support
2351+
## JEXL support in custom notifications
23452352

23462353
Orion Context Broker supports [JEXL expressions](https://github.com/TomFrost/Jexl) in custom notification [macro replacement](#macro-substitution). Thus, subscriptions like this can be defined:
23472354

@@ -5031,6 +5038,7 @@ A `condition` contains the following subfields:
50315038
|--------------|----------|-------|-------------------------------------------------------------------------------------------------------------------------------|
50325039
| `attrs` || array | Array of attribute names that will trigger the notification. Empty list is not allowed. |
50335040
| `expression` || object| An expression composed of `q`, `mq`, `georel`, `geometry` and `coords` (see [List Entities](#list-entities-get-v2entities) operation above about this field). `expression` and sub elements (i.e. `q`) must have content, i.e. `{}` or `""` is not allowed. `georel`, `geometry` and `coords` have to be used together (i.e. "all or nothing"). Check the example using geoquery as expression [below](#create-subscription-post-v2subscriptions).|
5041+
| `jexlExpression` || string | JEXL expression evaluated to determine whether a notification must be sent. If this field is used (note it is optional), the notification is triggered only when the expression evaluates to `true`. It works in the same way as the [JEXL expressions in custom notifications](#jexl-support-in-custom-notifications).|
50345042
| `alterationTypes` || array | Specify under which alterations (entity creation, entity modification, etc.) the subscription is triggered (see section [Subscriptions based in alteration type](#subscriptions-based-in-alteration-type)) |
50355043
| `notifyOnMetadataChange` || boolean | If `true` then metadata is considered part of the value of the attribute in the context of notification, so if the value doesn't change but the metadata changes, then a notification is triggered. If `false` then the metadata is not considered part of the value of the attribute in the context of notification, so if the value doesn't change but the metadata changes, then a notification is not triggered. Default value is `true`. |
50365044

src/lib/apiTypesV2/Subscription.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,11 @@ std::string Condition::toJson()
322322

323323
jh.addRaw("attrs", vectorToJson(this->attributes));
324324

325+
if (!this->jexlExpression.empty())
326+
{
327+
jh.addString("jexlExpression", this->jexlExpression);
328+
}
329+
325330
JsonObjectHelper jhe;
326331

327332
if (!this->expression.q.empty()) jhe.addString("q", this->expression.q);

src/lib/apiTypesV2/Subscription.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ struct Condition
126126
{
127127
std::vector<std::string> attributes;
128128
Expression expression;
129+
std::string jexlExpression;
129130
std::vector<SubAltType> altTypes;
130131
bool notifyOnMetadataChange;
131132
std::string toJson();

src/lib/cache/subCache.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,7 @@ void subCacheItemInsert
861861
StringFilter* mdStringFilterP,
862862
const std::string& status,
863863
double statusLastChange,
864+
const std::string& jexlExpression,
864865
const std::string& q,
865866
const std::string& geometry,
866867
const std::string& coords,
@@ -899,6 +900,7 @@ void subCacheItemInsert
899900
cSubP->failsCounter = 0;
900901
cSubP->status = status;
901902
cSubP->statusLastChange = statusLastChange;
903+
cSubP->jexlExpression = jexlExpression;
902904
cSubP->expression.q = q;
903905
cSubP->expression.geometry = geometry;
904906
cSubP->expression.coords = coords;

src/lib/cache/subCache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ struct CachedSubscription
111111
double statusLastChange;
112112
int64_t count;
113113
RenderFormat renderFormat;
114+
std::string jexlExpression;
114115
Expression expression;
115116
bool blacklist;
116117
bool onlyChanged;
@@ -218,6 +219,7 @@ extern void subCacheItemInsert
218219
StringFilter* mdStringFilterP,
219220
const std::string& status,
220221
double statusLastChange,
222+
const std::string& jexlExpression,
221223
const std::string& q,
222224
const std::string& geometry,
223225
const std::string& coords,

src/lib/jsonParseV2/parseSubscription.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,6 +1511,17 @@ static std::string parseNotifyConditionVector
15111511
}
15121512
}
15131513

1514+
// jexlExpression
1515+
if (condition.HasMember("jexlExpression"))
1516+
{
1517+
const rapidjson::Value& jexlExpression = condition["jexlExpression"];
1518+
if (!jexlExpression.IsString())
1519+
{
1520+
return badInput(ciP, "jexlExpression is not a string");
1521+
}
1522+
subsP->subject.condition.jexlExpression = jexlExpression.GetString();
1523+
}
1524+
15141525
// Expression
15151526
if (condition.HasMember("expression"))
15161527
{

src/lib/mongoBackend/MongoCommonSubscription.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,18 @@ void setExpression(const Subscription& sub, orion::BSONObjBuilder* b)
676676

677677

678678

679+
/* ****************************************************************************
680+
*
681+
* setJexlExpression -
682+
*/
683+
void setJexlExpression(const Subscription& sub, orion::BSONObjBuilder* b)
684+
{
685+
b->append(CSUB_JEXL_EXPR, sub.subject.condition.jexlExpression);
686+
LM_T(LmtMongo, ("Subscription jexlExpression: %s", sub.subject.condition.jexlExpression.c_str()));
687+
}
688+
689+
690+
679691
/* ****************************************************************************
680692
*
681693
* setFormat -

0 commit comments

Comments
 (0)