Skip to content

Commit 29a673b

Browse files
committed
Update Evaluator
1 parent de8d76d commit 29a673b

File tree

4 files changed

+63
-7
lines changed

4 files changed

+63
-7
lines changed

client/src/main/java/io/split/engine/evaluator/EvaluatorImp.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.split.client.exceptions.ChangeNumberExceptionWrapper;
55
import io.split.engine.experiments.ParsedCondition;
66
import io.split.engine.experiments.ParsedSplit;
7+
import io.split.engine.matchers.PrerequisitesMatcher;
78
import io.split.engine.splitter.Splitter;
89
import io.split.grammar.Treatments;
910
import io.split.storages.RuleBasedSegmentCacheConsumer;
@@ -27,6 +28,7 @@ public class EvaluatorImp implements Evaluator {
2728
private final RuleBasedSegmentCacheConsumer _ruleBasedSegmentCacheConsumer;
2829
private final EvaluationContext _evaluationContext;
2930
private final SplitCacheConsumer _splitCacheConsumer;
31+
private PrerequisitesMatcher _prerequisitesMatcher;
3032

3133
public EvaluatorImp(SplitCacheConsumer splitCacheConsumer, SegmentCacheConsumer segmentCache,
3234
RuleBasedSegmentCacheConsumer ruleBasedSegmentCacheConsumer) {
@@ -88,8 +90,8 @@ private List<String> getFeatureFlagNamesByFlagSets(List<String> flagSets) {
8890
private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bucketingKey, ParsedSplit parsedSplit, Map<String,
8991
Object> attributes) throws ChangeNumberExceptionWrapper {
9092
try {
93+
String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null;
9194
if (parsedSplit.killed()) {
92-
String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null;
9395
return new TreatmentLabelAndChangeNumber(
9496
parsedSplit.defaultTreatment(),
9597
Labels.KILLED,
@@ -98,6 +100,17 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu
98100
parsedSplit.impressionsDisabled());
99101
}
100102

103+
String bk = (bucketingKey == null) ? matchingKey : bucketingKey;
104+
105+
if (!_prerequisitesMatcher.match(matchingKey, bk, attributes, _evaluationContext)) {
106+
return new TreatmentLabelAndChangeNumber(
107+
parsedSplit.defaultTreatment(),
108+
Labels.PREREQUISITES_NOT_MET,
109+
parsedSplit.changeNumber(),
110+
config,
111+
parsedSplit.impressionsDisabled());
112+
}
113+
101114
/*
102115
* There are three parts to a single Feature flag: 1) Whitelists 2) Traffic Allocation
103116
* 3) Rollout. The flag inRollout is there to understand when we move into the Rollout
@@ -106,8 +119,6 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu
106119
*/
107120
boolean inRollout = false;
108121

109-
String bk = (bucketingKey == null) ? matchingKey : bucketingKey;
110-
111122
for (ParsedCondition parsedCondition : parsedSplit.parsedConditions()) {
112123

113124
if (!inRollout && parsedCondition.conditionType() == ConditionType.ROLLOUT) {
@@ -118,7 +129,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu
118129

119130
if (bucket > parsedSplit.trafficAllocation()) {
120131
// out of split
121-
String config = parsedSplit.configurations() != null ?
132+
config = parsedSplit.configurations() != null ?
122133
parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null;
123134
return new TreatmentLabelAndChangeNumber(parsedSplit.defaultTreatment(), Labels.NOT_IN_SPLIT,
124135
parsedSplit.changeNumber(), config, parsedSplit.impressionsDisabled());
@@ -130,7 +141,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu
130141

131142
if (parsedCondition.matcher().match(matchingKey, bucketingKey, attributes, _evaluationContext)) {
132143
String treatment = Splitter.getTreatment(bk, parsedSplit.seed(), parsedCondition.partitions(), parsedSplit.algo());
133-
String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(treatment) : null;
144+
config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(treatment) : null;
134145
return new TreatmentLabelAndChangeNumber(
135146
treatment,
136147
parsedCondition.label(),
@@ -140,7 +151,7 @@ private TreatmentLabelAndChangeNumber getTreatment(String matchingKey, String bu
140151
}
141152
}
142153

143-
String config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null;
154+
config = parsedSplit.configurations() != null ? parsedSplit.configurations().get(parsedSplit.defaultTreatment()) : null;
144155
return new TreatmentLabelAndChangeNumber(
145156
parsedSplit.defaultTreatment(),
146157
Labels.DEFAULT_RULE,
@@ -158,7 +169,7 @@ private TreatmentLabelAndChangeNumber evaluateParsedSplit(String matchingKey, St
158169
if (parsedSplit == null) {
159170
return new TreatmentLabelAndChangeNumber(Treatments.CONTROL, Labels.DEFINITION_NOT_FOUND);
160171
}
161-
172+
_prerequisitesMatcher = new PrerequisitesMatcher(parsedSplit.prerequisites());
162173
return getTreatment(matchingKey, bucketingKey, parsedSplit, attributes);
163174
} catch (ChangeNumberExceptionWrapper e) {
164175
_log.error("Evaluator Exception", e.wrappedException());

client/src/main/java/io/split/engine/evaluator/Labels.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ public class Labels {
77
public static final String DEFINITION_NOT_FOUND = "definition not found";
88
public static final String EXCEPTION = "exception";
99
public static final String UNSUPPORTED_MATCHER = "targeting rule type unsupported by sdk";
10+
public static final String PREREQUISITES_NOT_MET = "prerequisites not met";
1011
}

client/src/main/java/io/split/engine/matchers/PrerequisitesMatcher.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ public boolean match(Object matchValue, String bucketingKey, Map<String, Object>
2424
if (!(matchValue instanceof String)) {
2525
return false;
2626
}
27+
28+
if (_prerequisites == null) {
29+
return true;
30+
}
31+
2732
for (Prerequisites prerequisites : _prerequisites) {
2833
String treatment = evaluationContext.getEvaluator().evaluateFeature((String) matchValue, bucketingKey,
2934
prerequisites.featureFlagName, attributes). treatment;

client/src/test/java/io/split/engine/evaluator/EvaluatorTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import io.split.client.dtos.ConditionType;
44
import io.split.client.dtos.Partition;
5+
import io.split.client.dtos.Prerequisites;
6+
import io.split.client.utils.Json;
57
import io.split.engine.experiments.ParsedCondition;
68
import io.split.engine.experiments.ParsedSplit;
79
import io.split.engine.matchers.CombiningMatcher;
@@ -186,4 +188,41 @@ public void evaluateWithSetsNotHaveFlags() {
186188
Map<String, EvaluatorImp.TreatmentLabelAndChangeNumber> result = _evaluator.evaluateFeaturesByFlagSets(MATCHING_KEY, BUCKETING_KEY, sets, null);
187189
Assert.assertTrue(result.isEmpty());
188190
}
191+
192+
@Test
193+
public void evaluateWithPrerequisites() {
194+
Partition partition = new Partition();
195+
partition.treatment = TREATMENT_VALUE;
196+
partition.size = 100;
197+
_partitions.add(partition);
198+
ParsedCondition condition = new ParsedCondition(ConditionType.WHITELIST, _matcher, _partitions, "test whitelist label");
199+
_conditions.add(condition);
200+
List<Prerequisites> prerequisites = Arrays.asList(Json.fromJson("{\"n\": \"split1\", \"ts\": [\"" + TREATMENT_VALUE + "\"]}", Prerequisites.class));
201+
202+
ParsedSplit split = new ParsedSplit(SPLIT_NAME, 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, prerequisites);
203+
ParsedSplit split1 = new ParsedSplit("split1", 0, false, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, null);
204+
205+
Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split);
206+
Mockito.when(_splitCacheConsumer.get("split1")).thenReturn(split1);
207+
Mockito.when(condition.matcher().match(Mockito.anyString(), Mockito.anyString(), Mockito.anyObject(), Mockito.anyObject())).thenReturn(true);
208+
209+
EvaluatorImp.TreatmentLabelAndChangeNumber result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null);
210+
assertEquals(TREATMENT_VALUE, result.treatment);
211+
assertEquals("test whitelist label", result.label);
212+
assertEquals(CHANGE_NUMBER, result.changeNumber);
213+
214+
Mockito.when(condition.matcher().match(Mockito.anyString(), Mockito.anyString(), Mockito.anyObject(), Mockito.anyObject())).thenReturn(false);
215+
result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null);
216+
assertEquals(DEFAULT_TREATMENT_VALUE, result.treatment);
217+
assertEquals(Labels.PREREQUISITES_NOT_MET, result.label);
218+
assertEquals(CHANGE_NUMBER, result.changeNumber);
219+
220+
// if split is killed, label should be killed.
221+
split = new ParsedSplit(SPLIT_NAME, 0, true, DEFAULT_TREATMENT_VALUE, _conditions, TRAFFIC_TYPE_VALUE, CHANGE_NUMBER, 60, 18, 2, _configurations, new HashSet<>(), true, prerequisites);
222+
Mockito.when(_splitCacheConsumer.get(SPLIT_NAME)).thenReturn(split);
223+
result = _evaluator.evaluateFeature(MATCHING_KEY, BUCKETING_KEY, SPLIT_NAME, null);
224+
assertEquals(DEFAULT_TREATMENT_VALUE, result.treatment);
225+
assertEquals(Labels.KILLED, result.label);
226+
assertEquals(CHANGE_NUMBER, result.changeNumber);
227+
}
189228
}

0 commit comments

Comments
 (0)