From af65cdcb28fced073e9a5af118fb620d6d79d234 Mon Sep 17 00:00:00 2001 From: Mauro Antonio Sanz Date: Wed, 30 Apr 2025 19:00:40 -0300 Subject: [PATCH 1/2] fixing excluded segments --- .../io/split/client/dtos/RuleBasedSegment.java | 2 -- .../engine/matchers/RuleBasedSegmentMatcher.java | 14 +++++++++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/client/src/main/java/io/split/client/dtos/RuleBasedSegment.java b/client/src/main/java/io/split/client/dtos/RuleBasedSegment.java index 75fc92bc..6643a4e7 100644 --- a/client/src/main/java/io/split/client/dtos/RuleBasedSegment.java +++ b/client/src/main/java/io/split/client/dtos/RuleBasedSegment.java @@ -18,8 +18,6 @@ public String toString() { ", status=" + status + ", trafficTypeName='" + trafficTypeName + '\'' + ", changeNumber=" + changeNumber + - ", excluded.keys=" + Arrays.toString(excluded.keys.stream().toArray()) + - ", excluded.segments=" + Arrays.toString(excluded.segments.stream().toArray()) + '}'; } } diff --git a/client/src/main/java/io/split/engine/matchers/RuleBasedSegmentMatcher.java b/client/src/main/java/io/split/engine/matchers/RuleBasedSegmentMatcher.java index f92b999a..0541b90d 100644 --- a/client/src/main/java/io/split/engine/matchers/RuleBasedSegmentMatcher.java +++ b/client/src/main/java/io/split/engine/matchers/RuleBasedSegmentMatcher.java @@ -19,6 +19,7 @@ */ public class RuleBasedSegmentMatcher implements Matcher { private final String standardType = "standard"; + private final String ruleBasedType = "rule-based"; private final String _segmentName; @@ -44,8 +45,19 @@ public boolean match(Object matchValue, String bucketingKey, Map if (segment.type.equals(standardType) && evaluationContext.getSegmentCache().isInSegment(segment.name, (String) matchValue)) { return false; } + + if (segment.type.equals(ruleBasedType)) { + List conditions = evaluationContext.getRuleBasedSegmentCache().get(segment.name).parsedConditions(); + if (matchConditions(conditions, matchValue, bucketingKey, attributes, evaluationContext)) { + return true; + } + } } - List conditions = parsedRuleBasedSegment.parsedConditions(); + + return matchConditions(parsedRuleBasedSegment.parsedConditions(), matchValue, bucketingKey, attributes, evaluationContext); + } + + private boolean matchConditions(List conditions, Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) { for (ParsedCondition parsedCondition : conditions) { if (parsedCondition.matcher().match((String) matchValue, bucketingKey, attributes, evaluationContext)) { return true; From b043ffbb2e48adc846c52a887388f4bbc1889992 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Wed, 30 Apr 2025 16:55:35 -0700 Subject: [PATCH 2/2] added tests --- .../split/client/dtos/RuleBasedSegment.java | 2 + .../matchers/RuleBasedSegmentMatcher.java | 3 +- .../matchers/RuleBasedSegmentMatcherTest.java | 35 ++++++++++- .../test/resources/rule_base_segments.json | 61 +++++++++++++++++++ 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 client/src/test/resources/rule_base_segments.json diff --git a/client/src/main/java/io/split/client/dtos/RuleBasedSegment.java b/client/src/main/java/io/split/client/dtos/RuleBasedSegment.java index 6643a4e7..75fc92bc 100644 --- a/client/src/main/java/io/split/client/dtos/RuleBasedSegment.java +++ b/client/src/main/java/io/split/client/dtos/RuleBasedSegment.java @@ -18,6 +18,8 @@ public String toString() { ", status=" + status + ", trafficTypeName='" + trafficTypeName + '\'' + ", changeNumber=" + changeNumber + + ", excluded.keys=" + Arrays.toString(excluded.keys.stream().toArray()) + + ", excluded.segments=" + Arrays.toString(excluded.segments.stream().toArray()) + '}'; } } diff --git a/client/src/main/java/io/split/engine/matchers/RuleBasedSegmentMatcher.java b/client/src/main/java/io/split/engine/matchers/RuleBasedSegmentMatcher.java index 0541b90d..e069493c 100644 --- a/client/src/main/java/io/split/engine/matchers/RuleBasedSegmentMatcher.java +++ b/client/src/main/java/io/split/engine/matchers/RuleBasedSegmentMatcher.java @@ -57,7 +57,8 @@ public boolean match(Object matchValue, String bucketingKey, Map return matchConditions(parsedRuleBasedSegment.parsedConditions(), matchValue, bucketingKey, attributes, evaluationContext); } - private boolean matchConditions(List conditions, Object matchValue, String bucketingKey, Map attributes, EvaluationContext evaluationContext) { + private boolean matchConditions(List conditions, Object matchValue, String bucketingKey, + Map attributes, EvaluationContext evaluationContext) { for (ParsedCondition parsedCondition : conditions) { if (parsedCondition.matcher().match((String) matchValue, bucketingKey, attributes, evaluationContext)) { return true; diff --git a/client/src/test/java/io/split/engine/matchers/RuleBasedSegmentMatcherTest.java b/client/src/test/java/io/split/engine/matchers/RuleBasedSegmentMatcherTest.java index 3e6ed517..8563eafd 100644 --- a/client/src/test/java/io/split/engine/matchers/RuleBasedSegmentMatcherTest.java +++ b/client/src/test/java/io/split/engine/matchers/RuleBasedSegmentMatcherTest.java @@ -4,21 +4,32 @@ import com.google.common.collect.Sets; import io.split.client.dtos.ConditionType; import io.split.client.dtos.MatcherCombiner; +import io.split.client.dtos.SplitChange; +import io.split.client.utils.Json; +import io.split.client.utils.RuleBasedSegmentsToUpdate; import io.split.engine.evaluator.EvaluationContext; import io.split.engine.evaluator.Evaluator; import io.split.engine.experiments.ParsedCondition; import io.split.engine.experiments.ParsedRuleBasedSegment; +import io.split.engine.experiments.RuleBasedSegmentParser; import io.split.engine.matchers.strings.WhitelistMatcher; import io.split.storages.RuleBasedSegmentCache; import io.split.storages.RuleBasedSegmentCacheConsumer; import io.split.storages.SegmentCache; import io.split.storages.memory.RuleBasedSegmentCacheInMemoryImp; import io.split.storages.memory.SegmentCacheInMemoryImpl; +import okhttp3.mockwebserver.MockResponse; import org.junit.Test; import org.mockito.Mockito; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.HashMap; import java.util.Set; +import static io.split.client.utils.RuleBasedSegmentProcessor.processRuleBasedSegmentChanges; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; @@ -47,7 +58,29 @@ public void works() { assertThat(matcher.match("foo", null, null, evaluationContext), is(false)); assertThat(matcher.match(null, null, null, evaluationContext), is(false)); - } + @Test + public void usingRbsWithinExcludedTest() throws IOException { + String load = new String(Files.readAllBytes(Paths.get("src/test/resources/rule_base_segments.json")), StandardCharsets.UTF_8); + Evaluator evaluator = Mockito.mock(Evaluator.class); + SegmentCache segmentCache = new SegmentCacheInMemoryImpl(); + RuleBasedSegmentCache ruleBasedSegmentCache = new RuleBasedSegmentCacheInMemoryImp(); + EvaluationContext evaluationContext = new EvaluationContext(evaluator, segmentCache, ruleBasedSegmentCache); + + SplitChange change = Json.fromJson(load, SplitChange.class); + RuleBasedSegmentParser ruleBasedSegmentParser = new RuleBasedSegmentParser(); + RuleBasedSegmentsToUpdate ruleBasedSegmentsToUpdate = processRuleBasedSegmentChanges(ruleBasedSegmentParser, + change.ruleBasedSegments.d); + ruleBasedSegmentCache.update(ruleBasedSegmentsToUpdate.getToAdd(), null, 123); + RuleBasedSegmentMatcher matcher = new RuleBasedSegmentMatcher("dependent_rbs"); + HashMap attrib1 = new HashMap() {{ + put("email", "mauro@@split.io"); + }}; + HashMap attrib2 = new HashMap() {{ + put("email", "bilal@@split.io"); + }}; + assertThat(matcher.match("mauro@split.io", null, attrib1, evaluationContext), is(false)); + assertThat(matcher.match("bilal@split.io", null, attrib2, evaluationContext), is(true)); + } } diff --git a/client/src/test/resources/rule_base_segments.json b/client/src/test/resources/rule_base_segments.json new file mode 100644 index 00000000..65cd9a5d --- /dev/null +++ b/client/src/test/resources/rule_base_segments.json @@ -0,0 +1,61 @@ +{"ff": {"d": [], "t": -1, "s": -1}, +"rbs": {"t": -1, "s": -1, "d": + [{ + "changeNumber": 5, + "name": "sample_rule_based_segment", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded":{"keys":["mauro@split.io","gaston@split.io"],"segments":[]}, + "conditions": [ + { + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": "email" + }, + "matcherType": "ENDS_WITH", + "negate": false, + "whitelistMatcherData": { + "whitelist": [ + "@split.io" + ] + } + } + ] + } + } + ]}, + { + "changeNumber": 5, + "name": "dependent_rbs", + "status": "ACTIVE", + "trafficTypeName": "user", + "excluded": { + "keys": [], + "segments": [] + }, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user" + }, + "matcherType": "IN_RULE_BASED_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "sample_rule_based_segment" + } + } + ] + } + } + ] + }] +}}