Skip to content

Commit 3325b73

Browse files
committed
FT calculator
1 parent 8f50a35 commit 3325b73

File tree

5 files changed

+152
-18
lines changed

5 files changed

+152
-18
lines changed

src/main/java/io/split/android/client/fallback/FallbackTreatment.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55

66
import java.util.Objects;
77

8+
import io.split.android.grammar.Treatments;
9+
810
/**
911
* Represents the fallback treatment, with an optional config and a fixed label.
1012
*/
1113
public final class FallbackTreatment {
1214

1315
public static final String LABEL = "fallback treatment";
1416

17+
/**
18+
* Default fallback representing "control" treatment with no config.
19+
*/
20+
public static final FallbackTreatment CONTROL = new FallbackTreatment(Treatments.CONTROL);
21+
1522
@NonNull
1623
private final String mTreatment;
1724
@Nullable
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.split.android.client.fallback;
2+
3+
import androidx.annotation.NonNull;
4+
5+
/**
6+
* Resolves a fallback treatment for a given flag name.
7+
* Returns null if no fallback applies (caller should use control).
8+
*/
9+
public interface FallbackTreatmentsCalculator {
10+
11+
/**
12+
* Resolve a fallback for a given flag name.
13+
*
14+
* @param flagName non-null flag name
15+
* @return a fallback treatment if configured, otherwise null
16+
*/
17+
@NonNull
18+
FallbackTreatment resolve(@NonNull String flagName);
19+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.split.android.client.fallback;
2+
3+
import androidx.annotation.NonNull;
4+
5+
import java.util.Map;
6+
7+
public final class FallbackTreatmentsCalculatorImpl implements FallbackTreatmentsCalculator {
8+
9+
@NonNull
10+
private final FallbackConfiguration mConfig;
11+
12+
public FallbackTreatmentsCalculatorImpl(@NonNull FallbackConfiguration config) {
13+
mConfig = config;
14+
}
15+
16+
@NonNull
17+
@Override
18+
public FallbackTreatment resolve(@NonNull String flagName) {
19+
Map<String, FallbackTreatment> byFlag = mConfig.getByFlag();
20+
if (byFlag != null) {
21+
FallbackTreatment flagTreatment = byFlag.get(flagName);
22+
if (flagTreatment != null) {
23+
return flagTreatment;
24+
}
25+
}
26+
FallbackTreatment global = mConfig.getGlobal();
27+
if (global != null) {
28+
return global;
29+
}
30+
return FallbackTreatment.CONTROL;
31+
}
32+
}

src/main/java/io/split/android/grammar/Treatments.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,7 @@
77
public class Treatments {
88

99
public static final String CONTROL = "control";
10-
11-
/**
12-
* OFF is a synonym for CONTROL.
13-
*/
1410
public static final String OFF = "off";
1511
public static final String ON = "on";
1612

17-
public static boolean isControl(String treatment) {
18-
return CONTROL.equals(treatment) || OFF.equals(treatment);
19-
}
20-
21-
public static String controlSynonym(String treatment) {
22-
if (!isControl(treatment)) {
23-
throw new IllegalArgumentException("Not a control treatment: " + treatment);
24-
}
25-
if (Treatments.OFF.equals(treatment)) {
26-
return Treatments.CONTROL;
27-
}
28-
return Treatments.OFF;
29-
}
30-
3113
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package io.split.android.client.fallback;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotNull;
5+
6+
import org.junit.Test;
7+
8+
import java.util.Collections;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
12+
public class FallbackTreatmentsCalculatorTest {
13+
14+
@Test
15+
public void flagLevelOverrideTakesPrecedence() {
16+
FallbackTreatment global = new FallbackTreatment("off", "{\"g\":true}");
17+
FallbackTreatment byFlag = new FallbackTreatment("on", "{\"f\":true}");
18+
Map<String, FallbackTreatment> map = new HashMap<>();
19+
map.put("my_flag", byFlag);
20+
FallbackConfiguration config = FallbackConfiguration.builder()
21+
.global(global)
22+
.byFlag(map)
23+
.build();
24+
25+
FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
26+
FallbackTreatment resolvedExisting = calculator.resolve("my_flag");
27+
FallbackTreatment resolvedOther = calculator.resolve("other_flag");
28+
29+
assertNotNull(resolvedExisting);
30+
assertEquals(byFlag, resolvedExisting);
31+
assertNotNull(resolvedOther);
32+
assertEquals(global, resolvedOther);
33+
}
34+
35+
@Test
36+
public void globalFallbackIsReturnedWhenNoFlagOverride() {
37+
FallbackTreatment global = new FallbackTreatment("off");
38+
FallbackConfiguration config = FallbackConfiguration.builder()
39+
.global(global)
40+
.byFlag(Collections.emptyMap())
41+
.build();
42+
43+
FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
44+
FallbackTreatment resolved = calculator.resolve("any_flag");
45+
46+
assertNotNull(resolved);
47+
assertEquals(global, resolved);
48+
}
49+
50+
@Test
51+
public void flagLevelFallbackIsReturnedWhenConfigured() {
52+
FallbackTreatment byFlag = new FallbackTreatment("on");
53+
Map<String, FallbackTreatment> map = new HashMap<>();
54+
map.put("flagA", byFlag);
55+
FallbackConfiguration config = FallbackConfiguration.builder()
56+
.byFlag(map)
57+
.build();
58+
59+
FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
60+
FallbackTreatment resolved = calculator.resolve("flagA");
61+
62+
assertNotNull(resolved);
63+
assertEquals(byFlag, resolved);
64+
}
65+
66+
@Test
67+
public void returnsControlWhenNoFallbackConfigured() {
68+
FallbackConfiguration config = FallbackConfiguration.builder()
69+
.build();
70+
71+
FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
72+
FallbackTreatment resolved = calculator.resolve("nope");
73+
74+
assertNotNull(resolved);
75+
assertEquals(FallbackTreatment.CONTROL, resolved);
76+
}
77+
78+
@Test
79+
public void nonexistentFlagFallsBackToGlobal() {
80+
FallbackTreatment global = new FallbackTreatment("off");
81+
Map<String, FallbackTreatment> map = new HashMap<>();
82+
map.put("flagA", new FallbackTreatment("on"));
83+
FallbackConfiguration config = FallbackConfiguration.builder()
84+
.global(global)
85+
.byFlag(map)
86+
.build();
87+
88+
FallbackTreatmentsCalculator calculator = new FallbackTreatmentsCalculatorImpl(config);
89+
FallbackTreatment resolved = calculator.resolve("flagB");
90+
91+
assertNotNull(resolved);
92+
assertEquals(global, resolved);
93+
}
94+
}

0 commit comments

Comments
 (0)