You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by dg...@apache.org on 2021/10/21 11:18:01 UTC

[unomi] branch UNOMI-518-fix-trackedConditions created (now a29af14)

This is an automated email from the ASF dual-hosted git repository.

dgriffon pushed a change to branch UNOMI-518-fix-trackedConditions
in repository https://gitbox.apache.org/repos/asf/unomi.git.


      at a29af14  UNOMI-518 : allow to use custom parameters in rules with tracked conditions

This branch includes the following new commits:

     new a29af14  UNOMI-518 : allow to use custom parameters in rules with tracked conditions

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[unomi] 01/01: UNOMI-518 : allow to use custom parameters in rules with tracked conditions

Posted by dg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

dgriffon pushed a commit to branch UNOMI-518-fix-trackedConditions
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit a29af149b91c206f9dc2517991f5eaf772f0354a
Author: David Griffon <dg...@jahia.com>
AuthorDate: Thu Oct 21 13:17:46 2021 +0200

    UNOMI-518 : allow to use custom parameters in rules with tracked conditions
---
 .../test/java/org/apache/unomi/itests/BaseIT.java  |  2 +
 .../org/apache/unomi/itests/RuleServiceIT.java     | 41 ++++++++++++++
 .../test/resources/testClickEventCondition.json    | 58 +++++++++++++++++++
 .../services/impl/rules/RulesServiceImpl.java      | 65 +++++++++++++++-------
 4 files changed, 146 insertions(+), 20 deletions(-)

diff --git a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
index 9f8a2d9..81d28da 100644
--- a/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/BaseIT.java
@@ -158,6 +158,8 @@ public abstract class BaseIT {
                         "src/test/resources/testCopyPropertiesWithoutSystemTags.json")),
                 replaceConfigurationFile("data/tmp/testLoginEventCondition.json", new File(
                         "src/test/resources/testLoginEventCondition.json")),
+                replaceConfigurationFile("data/tmp/testClickEventCondition.json", new File(
+                        "src/test/resources/testClickEventCondition.json")),
                 replaceConfigurationFile("data/tmp/testRuleGroovyAction.json", new File(
                         "src/test/resources/testRuleGroovyAction.json")),
                 replaceConfigurationFile("data/tmp/groovy/UpdateAddressAction.groovy", new File(
diff --git a/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java b/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
index 7a4488b..e94e88a 100644
--- a/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
@@ -17,9 +17,13 @@
 package org.apache.unomi.itests;
 
 import org.apache.unomi.api.*;
+import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.api.conditions.ConditionType;
 import org.apache.unomi.api.rules.Rule;
 import org.apache.unomi.api.services.EventService;
 import org.apache.unomi.api.services.RulesService;
+import org.apache.unomi.persistence.spi.CustomObjectMapper;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -32,7 +36,9 @@ import org.slf4j.LoggerFactory;
 
 import javax.inject.Inject;
 
+import java.io.File;
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.util.*;
 
 import static org.junit.Assert.*;
@@ -176,6 +182,41 @@ public class RuleServiceIT extends BaseIT {
         return new Event(UUID.randomUUID().toString(), "view", session, profile, TEST_SCOPE, sourceItem, targetItem, new Date());
     }
 
+    @Test
+    public void testGetTrackedConditions() throws InterruptedException, IOException {
+        // Add custom condition with parameter
+        ConditionType conditionType = CustomObjectMapper.getObjectMapper().readValue(
+                new File("data/tmp/testClickEventCondition.json").toURI().toURL(), ConditionType.class);
+        definitionsService.setConditionType(conditionType);
+        refreshPersistence();
+        rulesService.refreshRules();
+        // Test tracked parameter
+        // Add rule that has a trackParameter condition that matches
+        ConditionBuilder builder = new ConditionBuilder(definitionsService);
+        Rule trackParameterRule = new Rule(new Metadata(TEST_SCOPE, "tracked-parameter-rule", "Tracked parameter rule", "A rule with tracked parameter"));
+        Condition trackedCondition = builder.condition("clickEventCondition").build();
+        trackedCondition.setParameter("tracked.properties.pageInfo.pagePath", "/test-page.html");
+        trackedCondition.getConditionType().getMetadata().getSystemTags().add("trackedCondition");
+        trackParameterRule.setCondition(trackedCondition);
+        rulesService.setRule(trackParameterRule);
+        // Add rule that has a trackParameter condition that does not match
+        Rule unTrackParameterRule = new Rule(new Metadata(TEST_SCOPE, "not-tracked-parameter-rule", "Not Tracked parameter rule", "A rule that has a parameter not tracked"));
+        Condition unTrackedCondition = builder.condition("clickEventCondition").build();
+        unTrackedCondition.setParameter("tracked.properties.pageInfo.pagePath", "/test-page-that-does-not-exist.html");
+        unTrackedCondition.getConditionType().getMetadata().getSystemTags().add("trackedCondition");
+        unTrackParameterRule.setCondition(unTrackedCondition);
+        rulesService.setRule(unTrackParameterRule);
+        refreshPersistence();
+        rulesService.refreshRules();
+        // Check that the given event return the tracked condition
+        Profile profile = new Profile(UUID.randomUUID().toString());
+        Session session = new Session(UUID.randomUUID().toString(), profile, new Date(), TEST_SCOPE);
+        Event viewEvent = generateViewEvent(session, profile);
+        Set<Condition> trackedConditions = rulesService.getTrackedConditions(viewEvent.getTarget());
+        Assert.assertTrue(trackedConditions.contains(trackedCondition));
+        Assert.assertFalse(trackedConditions.contains(unTrackedCondition));
+    }
+
     @Override
     public void updateServices() throws InterruptedException {
         super.updateServices();
diff --git a/itests/src/test/resources/testClickEventCondition.json b/itests/src/test/resources/testClickEventCondition.json
new file mode 100644
index 0000000..d99e4b7
--- /dev/null
+++ b/itests/src/test/resources/testClickEventCondition.json
@@ -0,0 +1,58 @@
+{
+  "metadata": {
+    "id": "clickEventCondition",
+    "name": "clickEventCondition",
+    "description": "",
+    "systemTags": [
+      "availableToEndUser",
+      "behavioral",
+      "profileTags",
+      "event",
+      "condition",
+      "eventCondition",
+      "usableInPastEventCondition",
+      "trackedCondition"
+    ],
+    "readOnly": true
+  },
+  "parentCondition": {
+    "type": "booleanCondition",
+    "parameterValues": {
+      "subConditions": [
+        {
+          "type": "eventTypeCondition",
+          "parameterValues": {
+            "eventTypeId": "click"
+          }
+        },
+        {
+          "type": "sourceEventPropertyCondition",
+          "parameterValues": {
+            "path": "parameter::tracked.properties.pageInfo.pagePath"
+          }
+        },
+        {
+          "type": "eventPropertyCondition",
+          "parameterValues": {
+            "propertyName": "target.itemId",
+            "propertyValue": "parameter::itemId",
+            "comparisonOperator": "equals"
+          }
+        }
+      ],
+      "operator": "and"
+    }
+  },
+  "parameters": [
+    {
+      "id": "tracked.properties.pageInfo.pagePath",
+      "type": "string",
+      "multivalued": false
+    },
+    {
+      "id": "itemId",
+      "type": "string",
+      "multivalued": false
+    }
+  ]
+}
\ No newline at end of file
diff --git a/services/src/main/java/org/apache/unomi/services/impl/rules/RulesServiceImpl.java b/services/src/main/java/org/apache/unomi/services/impl/rules/RulesServiceImpl.java
index 01ae985..8cf3a1f 100644
--- a/services/src/main/java/org/apache/unomi/services/impl/rules/RulesServiceImpl.java
+++ b/services/src/main/java/org/apache/unomi/services/impl/rules/RulesServiceImpl.java
@@ -17,6 +17,7 @@
 
 package org.apache.unomi.services.impl.rules;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.unomi.api.Event;
 import org.apache.unomi.api.Item;
 import org.apache.unomi.api.Metadata;
@@ -45,6 +46,7 @@ import java.util.concurrent.TimeUnit;
 public class RulesServiceImpl implements RulesService, EventListenerService, SynchronousBundleListener {
 
     public static final String RULE_QUERY_PREFIX = "rule_";
+    public static final String TRACKED_PARAMETER_PREFIX = "tracked.";
     private static final Logger logger = LoggerFactory.getLogger(RulesServiceImpl.class.getName());
 
     private BundleContext bundleContext;
@@ -57,14 +59,14 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
     private ActionExecutorDispatcher actionExecutorDispatcher;
     private List<Rule> allRules;
 
-    private Map<String,RuleStatistics> allRuleStatistics = new ConcurrentHashMap<>();
+    private Map<String, RuleStatistics> allRuleStatistics = new ConcurrentHashMap<>();
 
     private Integer rulesRefreshInterval = 1000;
     private Integer rulesStatisticsRefreshInterval = 10000;
 
     private List<RuleListenerService> ruleListeners = new CopyOnWriteArrayList<RuleListenerService>();
 
-    private Map<String,Set<Rule>> rulesByEventType = new HashMap<>();
+    private Map<String, Set<Rule>> rulesByEventType = new HashMap<>();
     private Boolean optimizedRulesActivated = true;
 
     public void setBundleContext(BundleContext bundleContext) {
@@ -282,8 +284,8 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         return rules;
     }
 
-    private Map<String,Set<Rule>> getRulesByEventType(List<Rule> rules) {
-        Map<String,Set<Rule>> newRulesByEventType = new HashMap<>();
+    private Map<String, Set<Rule>> getRulesByEventType(List<Rule> rules) {
+        Map<String, Set<Rule>> newRulesByEventType = new HashMap<>();
         for (Rule rule : rules) {
             updateRulesByEventType(newRulesByEventType, rule);
         }
@@ -313,7 +315,7 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
             changes |= eventService.send(ruleFired);
 
             RuleStatistics ruleStatistics = getLocalRuleStatistics(rule);
-            ruleStatistics.setLocalExecutionCount(ruleStatistics.getLocalExecutionCount()+1);
+            ruleStatistics.setLocalExecutionCount(ruleStatistics.getLocalExecutionCount() + 1);
             ruleStatistics.setLocalActionsTime(ruleStatistics.getLocalActionsTime() + totalActionsTime);
             this.allRuleStatistics.put(rule.getItemId(), ruleStatistics);
         }
@@ -328,14 +330,14 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         return persistenceService.load(ruleId, RuleStatistics.class);
     }
 
-    public Map<String,RuleStatistics> getAllRuleStatistics() {
+    public Map<String, RuleStatistics> getAllRuleStatistics() {
         return allRuleStatistics;
     }
 
     @Override
     public void resetAllRuleStatistics() {
         Condition matchAllCondition = new Condition(definitionsService.getConditionType("matchAllCondition"));
-        persistenceService.removeByQuery(matchAllCondition,RuleStatistics.class);
+        persistenceService.removeByQuery(matchAllCondition, RuleStatistics.class);
         allRuleStatistics.clear();
     }
 
@@ -348,7 +350,7 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
     }
 
     public PartialList<Metadata> getRuleMetadatas(Query query) {
-        if(query.isForceRefresh()){
+        if (query.isForceRefresh()) {
             persistenceService.refresh();
         }
         definitionsService.resolveConditionType(query.getCondition());
@@ -394,24 +396,47 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         persistenceService.save(rule);
     }
 
-    public Set<Condition> getTrackedConditions(Item source){
+    public Set<Condition> getTrackedConditions(Item source) {
         Set<Condition> trackedConditions = new HashSet<>();
         for (Rule r : allRules) {
             if (!r.getMetadata().isEnabled()) {
                 continue;
             }
-            Condition trackedCondition = definitionsService.extractConditionBySystemTag(r.getCondition(), "trackedCondition");
-            if(trackedCondition != null){
-                Condition sourceEventPropertyCondition = definitionsService.extractConditionBySystemTag(r.getCondition(), "sourceEventCondition");
-                if(source != null && sourceEventPropertyCondition != null) {
-                    ParserHelper.resolveConditionType(definitionsService, sourceEventPropertyCondition, "rule " + r.getItemId() + " source event condition");
-                    if(persistenceService.testMatch(sourceEventPropertyCondition, source)){
+            Condition ruleCondition = r.getCondition();
+            Condition trackedCondition = definitionsService.extractConditionBySystemTag(ruleCondition, "trackedCondition");
+            if (trackedCondition != null) {
+                Condition evalCondition = definitionsService.extractConditionBySystemTag(ruleCondition, "sourceEventCondition");
+                if (evalCondition != null) {
+                    if (persistenceService.testMatch(evalCondition, source)) {
                         trackedConditions.add(trackedCondition);
                     }
                 } else {
-                    trackedConditions.add(trackedCondition);
+                    // lookup for track parameters
+                    Map<String, Object> trackedParameters = new HashMap<>();
+                    trackedCondition.getParameterValues().forEach((key, value) -> {
+                        if (!TRACKED_PARAMETER_PREFIX.equals(key) && StringUtils.startsWith(key, TRACKED_PARAMETER_PREFIX)) {
+                            trackedParameters.put(StringUtils.substringAfter(key, TRACKED_PARAMETER_PREFIX), value);
+                        }
+                    });
+                    if (trackedParameters.size() > 0) {
+                        evalCondition = new Condition(definitionsService.getConditionType("booleanCondition"));
+                        evalCondition.setParameter("operator", "and");
+                        ArrayList<Condition> conditions = new ArrayList<>();
+                        trackedParameters.forEach((key, value) -> {
+                            Condition propCondition = new Condition(definitionsService.getConditionType("eventPropertyCondition"));
+                            propCondition.setParameter("comparisonOperator", "equals");
+                            propCondition.setParameter("propertyName", key);
+                            propCondition.setParameter("propertyValue", value);
+                            conditions.add(propCondition);
+                        });
+                        evalCondition.setParameter("subConditions", conditions);
+                        if (persistenceService.testMatch(evalCondition, source)) {
+                            trackedConditions.add(trackedCondition);
+                        }
+                    } else {
+                        trackedConditions.add(trackedCondition);
+                    }
                 }
-
             }
         }
         return trackedConditions;
@@ -428,7 +453,7 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
                 refreshRules();
             }
         };
-        schedulerService.getScheduleExecutorService().scheduleWithFixedDelay(task, 0,rulesRefreshInterval, TimeUnit.MILLISECONDS);
+        schedulerService.getScheduleExecutorService().scheduleWithFixedDelay(task, 0, rulesRefreshInterval, TimeUnit.MILLISECONDS);
 
         TimerTask statisticsTask = new TimerTask() {
             @Override
@@ -456,7 +481,7 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
 
     private void syncRuleStatistics() {
         List<RuleStatistics> allPersistedRuleStatisticsList = persistenceService.getAllItems(RuleStatistics.class);
-        Map<String,RuleStatistics> allPersistedRuleStatistics = new HashMap<>();
+        Map<String, RuleStatistics> allPersistedRuleStatistics = new HashMap<>();
         for (RuleStatistics ruleStatistics : allPersistedRuleStatisticsList) {
             allPersistedRuleStatistics.put(ruleStatistics.getItemId(), ruleStatistics);
         }
@@ -543,7 +568,7 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         }
     }
 
-    private void updateRulesByEventType(Map<String,Set<Rule>> rulesByEventType, Rule rule) {
+    private void updateRulesByEventType(Map<String, Set<Rule>> rulesByEventType, Rule rule) {
         Set<String> eventTypeIds = ParserHelper.resolveConditionEventTypes(rule.getCondition());
         for (String eventTypeId : eventTypeIds) {
             Set<Rule> rules = rulesByEventType.get(eventTypeId);