Jonathan Sinovassin-Naïk created UNOMI-982: ----------------------------------------------
Summary: A condition with an unset optional parameter never matches
Key: UNOMI-982
URL: https://issues.apache.org/jira/browse/UNOMI-982
Project: Apache Unomi
Issue Type: Bug
Affects Versions: unomi-3.1.0
Reporter: Jonathan Sinovassin-Naïk
A condition type can declare an optional parameter. Its parent condition then
compares a field to parameter::<name>. When the caller supplies no value for
that name, the comparison states constraint, so the evaluator has to ignore the
comparison. This is how pageViewEventCondition makes pagePath and language
optional.
Apache Unomi 3.1 evaluates the comparison instead of ignoring it. The
comparison becomes field equals null, which matches nothing. A booleanCondition
with the and operator holds that comparison, so the whole condition never
matches, and every rule built on the condition type stops firing.
The failure is silent. Unomi logs no warning and no error, the rule stays
enabled and valid in persistence, and the REST API returns it unchanged.
Where the behaviour changed
UNOMI-883 changed three places, at
https://github.com/apache/unomi/commit/901b5fcdc62a7732097f7db9305dd9404356f8a1
and https://github.com/apache/unomi/pull/773.
ConditionContextHelper.parseParameterWithValidationRecursive keeps the
unresolved value in the parameter map. Before that commit, an unresolved
parameter reference voided the whole condition:
{code:java}
Object parameter = parseParameter(context, entry.getValue(), scriptExecutor);
if (parameter == null) {
return null;
}
{code}
ConditionEvaluatorDispatcherImpl.eval answers false for a null contextual
condition. Before that commit it answered true, which means the caller drops
the condition:
{code:java}
Condition contextualCondition =
ConditionContextHelper.getContextualCondition(condition, context,
scriptExecutor);
if (contextualCondition != null) {
return evaluator.eval(contextualCondition, item, context, dispatcher);
} else {
return true;
}
{code}
ConditionESQueryBuilderDispatcher.buildFilter answers a match-none query for
a null contextual condition. Before that commit it answered a match-all query.
ConditionOSQueryBuilderDispatcher.buildFilter
has the same shape.
Steps to reproduce
1. Start Apache Unomi 3.1.0-SNAPSHOT.
2. Register a condition type whose parent condition holds an optional
parameter. Post this document to /cxs/definitions/conditions:
{code:java}
{
"metadata": { "id": "pageViewEventCondition", "name":
"pageViewEventCondition",
"systemTags": ["condition", "eventCondition", "event"] },
"parentCondition": {
"type": "booleanCondition",
"parameterValues": {
"operator": "and",
"subConditions": [
{ "type": "eventTypeCondition", "parameterValues": { "eventTypeId":
"view" } },
{ "type": "eventPropertyCondition", "parameterValues": {
"propertyName": "target.properties.pageInfo.pagePath",
"propertyValue": "parameter::pagePath",
"comparisonOperator": "equals" } }
]
}
},
"parameters": [ { "id": "pagePath", "type": "string", "multivalued": false
} ]
}
{code}
3. Register a rule that uses the condition type and sets no pagePath. Post this
document to /cxs/rules:
{code:java}
{
"metadata": { "id": "copyURLParams", "name": "Copy URL params to profile",
"scope": "systemscope" },
"condition": { "type": "pageViewEventCondition", "parameterValues": {} },
"actions": [ { "type": "copyPropertiesAction",
"parameterValues": { "rootProperty":
"flattenedProperties.URLParameters",
"singleValueStrategy": "alwaysSet" } } ]
}
{code}
4. Send a view event to /cxs/context.json with a session id and no profile
cookie:
{code:java}
{ "sessionId": "<a new uuid>",
"events": [ { "eventType": "view", "scope": "test",
"target": { "scope": "test", "itemId": "page1", "itemType":
"page" },
"flattenedProperties": { "URLParameters": { "utm_content":
"testParamValue" } } } ] }
{code}
5. Read the profile named by the Set-Cookie header of the response.
Expected result
The rule matches the event, so the profile carries utm_content.
Actual result
The rule does not match, so the profile carries no utm_content. Replace the
rule condition with eventTypeCondition and eventTypeId view, and the same event
now copies the property. The event, the
action and the rules engine therefore all work.
The evaluation reaches the comparison and rejects it. Raise
org.apache.unomi.persistence.spi.conditions to DEBUG to see the line:
DEBUG ConditionContextHelper | Parameter reference 'pagePath' not found in
context
Scope
Every condition type that declares an optional parameter is affected. In the
Jahia jExperience module, four rules stop firing on Apache Unomi 3.1:
copyURLParams, incrementPageViewCount,
incrementInterests and the target event of a goal.
The query builders carry the same inversion. A segment or a past event
condition that reaches an optional parameter now selects nothing, where it used
to select everything.
Proposed fix
getContextualCondition gives three answers instead of two:
- the resolved condition,
- null, when the context does not supply a parameter reference the condition
carries,
- a new UNRESOLVABLE sentinel, when resolution itself failed.
A cyclic reference, a depth overrun and a script that did not run all produce
UNRESOLVABLE. The callers ignore a null condition and refuse an UNRESOLVABLE
one. UNOMI-883 therefore keeps its guard on
the broken cases, and the optional parameter works again.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)
