Skip to content

Commit 57cac15

Browse files
committed
Added models and updated config and validator
1 parent 5d33eb3 commit 57cac15

File tree

12 files changed

+320
-49
lines changed

12 files changed

+320
-49
lines changed

client/src/main/java/io/split/client/SplitClientConfig.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.split.client;
22

3+
import io.split.client.dtos.FallbackTreatment;
4+
import io.split.client.dtos.FallbackTreatmentsConfiguration;
35
import io.split.client.dtos.ProxyConfiguration;
46
import io.split.client.impressions.ImpressionListener;
57
import io.split.client.impressions.ImpressionsManager;
@@ -20,6 +22,8 @@
2022
import java.util.concurrent.ThreadFactory;
2123
import java.io.InputStream;
2224

25+
import static io.split.inputValidation.FallbackTreatmentValidator.isValidByFlagTreatment;
26+
import static io.split.inputValidation.FallbackTreatmentValidator.isValidTreatment;
2327
import static io.split.inputValidation.FlagSetsValidator.cleanup;
2428

2529
/**
@@ -91,6 +95,7 @@ private HttpScheme() {
9195
private final CustomStorageWrapper _customStorageWrapper;
9296
private final StorageMode _storageMode;
9397
private final ThreadFactory _threadFactory;
98+
private final FallbackTreatmentsConfiguration _fallbackTreatments;
9499

95100
// Proxy configs
96101
private final ProxyConfiguration _proxyConfiguration;
@@ -163,7 +168,8 @@ private SplitClientConfig(String endpoint,
163168
HashSet<String> flagSetsFilter,
164169
int invalidSets,
165170
CustomHeaderDecorator customHeaderDecorator,
166-
CustomHttpModule alternativeHTTPModule) {
171+
CustomHttpModule alternativeHTTPModule,
172+
FallbackTreatmentsConfiguration fallbackTreatments) {
167173
_endpoint = endpoint;
168174
_eventsEndpoint = eventsEndpoint;
169175
_featuresRefreshRate = pollForFeatureChangesEveryNSeconds;
@@ -218,6 +224,7 @@ private SplitClientConfig(String endpoint,
218224
_invalidSets = invalidSets;
219225
_customHeaderDecorator = customHeaderDecorator;
220226
_alternativeHTTPModule = alternativeHTTPModule;
227+
_fallbackTreatments = fallbackTreatments;
221228

222229
Properties props = new Properties();
223230
try {
@@ -436,6 +443,8 @@ public boolean isSdkEndpointOverridden() {
436443

437444
public CustomHttpModule alternativeHTTPModule() { return _alternativeHTTPModule; }
438445

446+
public FallbackTreatmentsConfiguration fallbackTreatments() { return _fallbackTreatments; }
447+
439448
public static final class Builder {
440449
private String _endpoint = SDK_ENDPOINT;
441450
private boolean _endpointSet = false;
@@ -494,6 +503,7 @@ public static final class Builder {
494503
private int _invalidSetsCount = 0;
495504
private CustomHeaderDecorator _customHeaderDecorator = null;
496505
private CustomHttpModule _alternativeHTTPModule = null;
506+
private FallbackTreatmentsConfiguration _fallbackTreatments;
497507

498508
public Builder() {
499509
}
@@ -1022,6 +1032,17 @@ public Builder alternativeHTTPModule(CustomHttpModule alternativeHTTPModule) {
10221032
return this;
10231033
}
10241034

1035+
/**
1036+
* Fallback Treatments
1037+
*
1038+
* @param fallbackTreatments
1039+
* @return this builder
1040+
*/
1041+
public Builder fallbackTreatments(FallbackTreatmentsConfiguration fallbackTreatments) {
1042+
_fallbackTreatments = fallbackTreatments;
1043+
return this;
1044+
}
1045+
10251046
/**
10261047
* Thread Factory
10271048
*
@@ -1158,6 +1179,21 @@ private void verifyProxy() {
11581179
}
11591180
}
11601181

1182+
private void verifyFallbackTreatments() {
1183+
if (_fallbackTreatments == null)
1184+
return;
1185+
1186+
if (_fallbackTreatments.getGlobalFallbackTreatment() != null) {
1187+
_fallbackTreatments.setGlobalFallbackTreatment(new FallbackTreatment(
1188+
isValidTreatment(_fallbackTreatments.getGlobalFallbackTreatment().getTreatment(), "config"),
1189+
_fallbackTreatments.getGlobalFallbackTreatment().getConfig()));
1190+
}
1191+
1192+
if (_fallbackTreatments.getByFlagFallbackTreatment() != null) {
1193+
_fallbackTreatments.setByFlagFallbackTreatment(isValidByFlagTreatment(_fallbackTreatments.getByFlagFallbackTreatment(), "config"));
1194+
}
1195+
}
1196+
11611197
public SplitClientConfig build() {
11621198

11631199
verifyRates();
@@ -1172,6 +1208,8 @@ public SplitClientConfig build() {
11721208

11731209
verifyProxy();
11741210

1211+
verifyFallbackTreatments();
1212+
11751213
if (_numThreadsForSegmentFetch <= 0) {
11761214
throw new IllegalArgumentException("Number of threads for fetching segments MUST be greater than zero");
11771215
}
@@ -1230,7 +1268,8 @@ public SplitClientConfig build() {
12301268
_flagSetsFilter,
12311269
_invalidSetsCount,
12321270
_customHeaderDecorator,
1233-
_alternativeHTTPModule);
1271+
_alternativeHTTPModule,
1272+
_fallbackTreatments);
12341273
}
12351274
}
12361275
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.split.client.dtos;
2+
3+
import java.util.Map;
4+
5+
public class FallbackTreatment {
6+
private final Map<String, Object> _config;
7+
private final String _treatment;
8+
private final String _label;
9+
10+
public FallbackTreatment(String treatment, Map<String, Object> config) {
11+
_treatment = treatment;
12+
_config = config;
13+
_label = "fallback - ";
14+
}
15+
16+
public FallbackTreatment(String treatment) {
17+
_treatment = treatment;
18+
_config = null;
19+
_label = "fallback - ";
20+
}
21+
22+
public Map<String, Object> getConfig() {
23+
return _config;
24+
}
25+
26+
public String getTreatment() {
27+
return _treatment;
28+
}
29+
30+
public String getLabel() {
31+
return _label;
32+
}
33+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.split.client.dtos;
2+
3+
import java.util.Map;
4+
5+
public class FallbackTreatmentsConfiguration {
6+
private FallbackTreatment _globalFallbackTreatment;
7+
private Map<String, FallbackTreatment> _byFlagFallbackTreatment;
8+
9+
public FallbackTreatmentsConfiguration(FallbackTreatment globalFallbackTreatment, Map<String, FallbackTreatment> byFlagFallbackTreatment) {
10+
_globalFallbackTreatment = globalFallbackTreatment;
11+
_byFlagFallbackTreatment = byFlagFallbackTreatment;
12+
}
13+
14+
public FallbackTreatment getGlobalFallbackTreatment() {
15+
return _globalFallbackTreatment;
16+
}
17+
public void setGlobalFallbackTreatment(FallbackTreatment newValue) {
18+
_globalFallbackTreatment = newValue;
19+
}
20+
21+
public Map<String, FallbackTreatment> getByFlagFallbackTreatment() { return _byFlagFallbackTreatment;}
22+
public void setByFlagFallbackTreatment(Map<String, FallbackTreatment> newValue) {
23+
_byFlagFallbackTreatment = newValue;
24+
}
25+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package io.split.inputValidation;
2+
3+
import io.split.client.dtos.FallbackTreatment;
4+
5+
import org.slf4j.Logger;
6+
import org.slf4j.LoggerFactory;
7+
8+
import java.util.HashMap;
9+
import java.util.Map;
10+
import java.util.Optional;
11+
import java.util.regex.Pattern;
12+
13+
import static io.split.inputValidation.SplitNameValidator.isValid;
14+
15+
public class FallbackTreatmentValidator {
16+
private static final Logger _log = LoggerFactory.getLogger(FallbackTreatmentValidator.class);
17+
private static final Pattern TREATMENT_MATCHER = Pattern.compile("^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$");
18+
private static final int MAX_LENGTH = 100;
19+
20+
public static String isValidTreatment(String name, String method) {
21+
if (name == null) {
22+
_log.error(String.format("%s: you passed a null treatment, fallback treatment must be a non-empty string", method));
23+
return null;
24+
}
25+
26+
if (name.isEmpty()) {
27+
_log.error(String.format("%s: you passed an empty treatment, fallback treatment must be a non-empty string", method));
28+
return null;
29+
}
30+
31+
String trimmed = name.trim();
32+
if (!trimmed.equals(name)) {
33+
_log.warn(String.format("%s: fallback treatment %s has extra whitespace, trimming", method, name));
34+
name = trimmed;
35+
}
36+
37+
if (!TREATMENT_MATCHER.matcher(name).find()) {
38+
_log.error(String.format("%s: you passed %s, treatment must adhere to the regular expression " +
39+
"^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$", method, name));
40+
return null;
41+
}
42+
43+
if (name.length() > MAX_LENGTH) {
44+
return null;
45+
}
46+
47+
return name;
48+
}
49+
50+
public static Map<String, FallbackTreatment> isValidByFlagTreatment(Map<String, FallbackTreatment> byFlagTreatment, String method) {
51+
Map<String, FallbackTreatment> result = new HashMap<>();
52+
for (Map.Entry<String, FallbackTreatment> entry : byFlagTreatment.entrySet()) {
53+
Optional<String> feature_name = isValid(entry.getKey(), "Validator");
54+
if (feature_name.equals(Optional.empty())) {
55+
continue;
56+
}
57+
58+
FallbackTreatment fallbackTreatment = entry.getValue();
59+
String treatment = isValidTreatment(fallbackTreatment.getTreatment(), "Validator");
60+
if (treatment == null) {
61+
continue;
62+
}
63+
64+
result.put(feature_name.get(), new FallbackTreatment(treatment, fallbackTreatment.getConfig()));
65+
}
66+
67+
return result;
68+
}
69+
}

client/src/main/java/io/split/inputValidation/SplitNameValidator.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
import java.util.List;
77
import java.util.Objects;
88
import java.util.Optional;
9+
import java.util.regex.Pattern;
910
import java.util.stream.Collectors;
1011

1112
public class SplitNameValidator {
1213
private static final Logger _log = LoggerFactory.getLogger(SplitNameValidator.class);
14+
private static final int MAX_LENGTH = 100;
15+
private static final Pattern NAME_MATCHER = Pattern.compile("^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$");
1316

1417
public static Optional<String> isValid(String name, String method) {
1518
if (name == null) {
@@ -28,6 +31,16 @@ public static Optional<String> isValid(String name, String method) {
2831
name = trimmed;
2932
}
3033

34+
if (name.length() > MAX_LENGTH) {
35+
return Optional.empty();
36+
}
37+
38+
if (!NAME_MATCHER.matcher(name).find()) {
39+
_log.error(String.format("%s: you passed %s, feature flag name must adhere to the regular expression " +
40+
"^[0-9]+[.a-zA-Z0-9_-]*$|^[a-zA-Z]+[a-zA-Z0-9_-]*$", method, name));
41+
return Optional.empty();
42+
}
43+
3144
return Optional.of(name);
3245
}
3346

client/src/test/java/io/split/client/SplitClientConfigTest.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
package io.split.client;
22

33
import com.google.common.util.concurrent.ThreadFactoryBuilder;
4-
import io.split.client.dtos.BasicCredentialsProvider;
5-
import io.split.client.dtos.BearerCredentialsProvider;
4+
import io.split.client.dtos.RequestContext;
5+
import io.split.client.dtos.FallbackTreatmentsConfiguration;
6+
import io.split.client.dtos.FallbackTreatment;
67
import io.split.client.dtos.ProxyConfiguration;
78
import io.split.client.impressions.Impression;
89
import io.split.client.impressions.ImpressionListener;
910
import io.split.client.impressions.ImpressionsManager;
10-
import io.split.client.dtos.RequestContext;
1111
import io.split.integrations.IntegrationsConfig;
1212
import org.junit.Assert;
1313
import org.junit.Test;
@@ -17,6 +17,7 @@
1717
import java.io.FileNotFoundException;
1818
import java.net.MalformedURLException;
1919
import java.net.URL;
20+
import java.util.HashMap;
2021
import java.util.List;
2122
import java.util.Map;
2223
import java.util.stream.Collectors;
@@ -362,4 +363,25 @@ public void mustUseP12PassKeyWithProxyMtls() throws MalformedURLException, FileN
362363
.build())
363364
.build();
364365
}
366+
367+
@Test
368+
public void fallbackTreatmentCheckRegex() {
369+
SplitClientConfig config = SplitClientConfig.builder()
370+
.fallbackTreatments(new FallbackTreatmentsConfiguration(new FallbackTreatment("12#2"), null))
371+
.build();
372+
Assert.assertEquals(null, config.fallbackTreatments().getGlobalFallbackTreatment().getTreatment());
373+
374+
config = SplitClientConfig.builder()
375+
.fallbackTreatments(new FallbackTreatmentsConfiguration(null, new HashMap<String, FallbackTreatment>() {{ put("flag", new FallbackTreatment("12#2")); }} ))
376+
.build();
377+
Assert.assertEquals(0, config.fallbackTreatments().getByFlagFallbackTreatment().size());
378+
379+
config = SplitClientConfig.builder()
380+
.fallbackTreatments(new FallbackTreatmentsConfiguration(
381+
new FallbackTreatment("on"),
382+
new HashMap<String, FallbackTreatment>() {{ put("flag", new FallbackTreatment("off")); }} ))
383+
.build();
384+
Assert.assertEquals("on", config.fallbackTreatments().getGlobalFallbackTreatment().getTreatment());
385+
Assert.assertEquals("off", config.fallbackTreatments().getByFlagFallbackTreatment().get("flag").getTreatment());
386+
}
365387
}

client/src/test/java/io/split/client/SplitClientIntegrationTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -712,10 +712,10 @@ public void testPluggableMode() throws IOException, URISyntaxException {
712712
Assert.assertTrue(events.stream().anyMatch(e -> "keyProperties".equals(e.getEventDto().key) && e.getEventDto().properties != null));
713713

714714
Assert.assertEquals(3, splits.size());
715-
Assert.assertTrue(splits.stream().anyMatch(sw -> "first.name".equals(sw.name)));
716-
Assert.assertTrue(splits.stream().anyMatch(sw -> "second.name".equals(sw.name)));
717-
Assert.assertEquals("on", client.getTreatment("key", "first.name"));
718-
Assert.assertEquals("off", client.getTreatmentWithConfig("FakeKey", "second.name").treatment());
715+
Assert.assertTrue(splits.stream().anyMatch(sw -> "first-name".equals(sw.name)));
716+
Assert.assertTrue(splits.stream().anyMatch(sw -> "second-name".equals(sw.name)));
717+
Assert.assertEquals("on", client.getTreatment("key", "first-name"));
718+
Assert.assertEquals("off", client.getTreatmentWithConfig("FakeKey", "second-name").treatment());
719719
Assert.assertEquals("control", client.getTreatment("FakeKey", "noSplit"));
720720
Assert.assertEquals("on", client.getTreatment("bilal@@split.io", "rbs_flag", new HashMap<String, Object>() {{
721721
put("email", "bilal@@split.io");
@@ -726,8 +726,8 @@ public void testPluggableMode() throws IOException, URISyntaxException {
726726

727727
List<ImpressionConsumer> impressions = customStorageWrapper.getImps();
728728
Assert.assertEquals(4, impressions.size());
729-
Assert.assertTrue(impressions.stream().anyMatch(imp -> "first.name".equals(imp.getKeyImpression().feature) && "on".equals(imp.getKeyImpression().treatment)));
730-
Assert.assertTrue(impressions.stream().anyMatch(imp -> "second.name".equals(imp.getKeyImpression().feature) && "off".equals(imp.getKeyImpression().treatment)));
729+
Assert.assertTrue(impressions.stream().anyMatch(imp -> "first-name".equals(imp.getKeyImpression().feature) && "on".equals(imp.getKeyImpression().treatment)));
730+
Assert.assertTrue(impressions.stream().anyMatch(imp -> "second-name".equals(imp.getKeyImpression().feature) && "off".equals(imp.getKeyImpression().treatment)));
731731

732732
Map<String, Long> latencies = customStorageWrapper.getLatencies();
733733

0 commit comments

Comments
 (0)