Skip to content

Commit 358cbd7

Browse files
committed
Added client, validation and DTO classes
1 parent 07d0ef4 commit 358cbd7

19 files changed

+1193
-268
lines changed

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

Lines changed: 474 additions & 0 deletions
Large diffs are not rendered by default.

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

Lines changed: 207 additions & 33 deletions
Large diffs are not rendered by default.

client/src/main/java/io/split/client/dtos/KeyImpression.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public class KeyImpression {
1515
/* package private */ static final String FIELD_TIME = "m";
1616
/* package private */ static final String FIELD_CHANGE_NUMBER = "c";
1717
/* package private */ static final String FIELD_PREVIOUS_TIME = "pt";
18+
/* package private */ static final String FIELD_PROPERTIES = "properties";
19+
20+
public static int MAX_PROPERTIES_LENGTH_BYTES = 32 * 1024;
1821

1922
public transient String feature; // Non-serializable
2023

@@ -39,6 +42,9 @@ public class KeyImpression {
3942
@SerializedName(FIELD_PREVIOUS_TIME)
4043
public Long previousTime;
4144

45+
@SerializedName(FIELD_PROPERTIES)
46+
public String properties;
47+
4248
@Override
4349
public boolean equals(Object o) {
4450
if (this == o) return true;
@@ -50,6 +56,7 @@ public boolean equals(Object o) {
5056
if (!Objects.equals(feature, that.feature)) return false;
5157
if (!keyName.equals(that.keyName)) return false;
5258
if (!treatment.equals(that.treatment)) return false;
59+
if (properties != null && !properties.equals(that.properties)) return false;
5360

5461
if (bucketingKey == null) {
5562
return that.bucketingKey == null;
@@ -78,6 +85,7 @@ public static KeyImpression fromImpression(Impression i) {
7885
ki.treatment = i.treatment();
7986
ki.label = i.appliedRule();
8087
ki.previousTime = i.pt();
88+
ki.properties = i.properties();
8189
return ki;
8290
}
8391
}

client/src/main/java/io/split/client/impressions/Impression.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ public class Impression {
1616
private final Long _changeNumber;
1717
private Long _pt;
1818
private final Map<String, Object> _attributes;
19+
private final String _properties;
1920

2021

2122
public Impression(String key, String bucketingKey, String featureFlag, String treatment, long time, String appliedRule,
22-
Long changeNumber, Map<String, Object> atributes) {
23+
Long changeNumber, Map<String, Object> atributes, String properties) {
2324
_key = key;
2425
_bucketingKey = bucketingKey;
2526
_split = featureFlag;
@@ -28,6 +29,7 @@ public Impression(String key, String bucketingKey, String featureFlag, String tr
2829
_appliedRule = appliedRule;
2930
_changeNumber = changeNumber;
3031
_attributes = atributes;
32+
_properties = properties;
3133
}
3234

3335
public String key() {
@@ -67,4 +69,8 @@ public Long pt() {
6769
}
6870

6971
public Impression withPreviousTime(Long pt) { _pt = pt; return this; }
72+
73+
public String properties() {
74+
return _properties;
75+
}
7076
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package io.split.inputValidation;
2+
3+
import io.split.client.dtos.KeyImpression;
4+
import org.json.JSONArray;
5+
import org.json.JSONException;
6+
import org.json.JSONObject;
7+
import org.slf4j.Logger;
8+
import org.slf4j.LoggerFactory;
9+
10+
import java.util.HashMap;
11+
import java.util.Iterator;
12+
import java.util.Map;
13+
14+
import static java.util.stream.Collectors.toList;
15+
16+
public class ImpressionPropertiesValidator {
17+
private static final Logger _log = LoggerFactory.getLogger(ImpressionPropertiesValidator.class);
18+
19+
public static ImpressionPropertiesValidatorResult propertiesAreValid(JSONObject properties) {
20+
int size = 1024; // We assume 1kb events without properties (750 bytes avg measured)
21+
22+
if (properties == null) {
23+
return new ImpressionPropertiesValidatorResult(true);
24+
}
25+
26+
if (toMap(properties).size() > 300) {
27+
_log.warn("Impression properties has more than 300 properties. Some of them will be trimmed when processed");
28+
}
29+
30+
Map<String, Object> result = new HashMap<>();
31+
for (Map.Entry<String, Object> entry : toMap(properties).entrySet()) {
32+
if (entry.getKey() == null || entry.getKey().isEmpty()) {
33+
continue;
34+
}
35+
36+
size += entry.getKey().length();
37+
Object value = entry.getValue();
38+
39+
if (!(value instanceof Number) && !(value instanceof Boolean) && !(value instanceof String)) {
40+
_log.warn(String.format("Property %s is of invalid type. Setting value to null", entry.getKey()));
41+
value = null;
42+
}
43+
44+
if (value instanceof String) {
45+
size += ((String) value).length();
46+
}
47+
48+
if (size > KeyImpression.MAX_PROPERTIES_LENGTH_BYTES) {
49+
_log.error(String.format("The maximum size allowed for the properties is 32768 bytes. "
50+
+ "Current one is %s bytes. Properties field is ignored", size));
51+
52+
return new ImpressionPropertiesValidatorResult(false);
53+
}
54+
55+
result.put(entry.getKey(), value);
56+
}
57+
58+
return new ImpressionPropertiesValidatorResult(true, size, result);
59+
}
60+
61+
public static Map<String, Object> toMap(JSONObject jsonobj) throws JSONException {
62+
Map<String, Object> map = new HashMap<String, Object>();
63+
Iterator<String> keys = jsonobj.keys();
64+
while(((java.util.Iterator<?>) keys).hasNext()) {
65+
String key = keys.next();
66+
Object value = jsonobj.get(key);
67+
if (value instanceof JSONArray) {
68+
value = toList();
69+
} else if (value instanceof JSONObject) {
70+
value = toMap((JSONObject) value);
71+
}
72+
map.put(key, value);
73+
} return map;
74+
}
75+
76+
public static class ImpressionPropertiesValidatorResult {
77+
private final boolean _success;
78+
private final int _propertySize;
79+
private final Map<String, Object> _value;
80+
81+
public ImpressionPropertiesValidatorResult(boolean success, int propertySize, Map<String, Object> value) {
82+
_success = success;
83+
_propertySize = propertySize;
84+
_value = value;
85+
}
86+
87+
public ImpressionPropertiesValidatorResult(boolean success) {
88+
_success = success;
89+
_propertySize = 0;
90+
_value = null;
91+
}
92+
93+
public boolean getSuccess() {
94+
return _success;
95+
}
96+
97+
public int getSize() {
98+
return _propertySize;
99+
}
100+
101+
public Map<String, Object> getValue() {
102+
return _value;
103+
}
104+
}
105+
}

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ public void attributesWork() {
565565
);
566566

567567
assertEquals("on", client.getTreatment("[email protected]", test));
568-
assertEquals("on", client.getTreatment("[email protected]", test, null));
568+
assertEquals("on", client.getTreatment("[email protected]", test, new HashMap<>()));
569569
assertEquals("on", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of()));
570570
assertEquals("on", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of("age", 10)));
571571
assertEquals("off", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of("age", 9)));
@@ -599,7 +599,7 @@ public void attributesWork2() {
599599
);
600600

601601
assertEquals("off", client.getTreatment("[email protected]", test));
602-
assertEquals("off", client.getTreatment("[email protected]", test, null));
602+
assertEquals("off", client.getTreatment("[email protected]", test, new HashMap<>()));
603603
assertEquals("off", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of()));
604604

605605
assertEquals("off", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of("age", 10)));
@@ -634,7 +634,7 @@ public void attributesGreaterThanNegativeNumber() {
634634
);
635635

636636
assertEquals("off", client.getTreatment("[email protected]", test));
637-
assertEquals("off", client.getTreatment("[email protected]", test, null));
637+
assertEquals("off", client.getTreatment("[email protected]", test, new HashMap<>()));
638638
assertEquals("off", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of()));
639639
assertEquals("off", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of("age", 10)));
640640
assertEquals("on", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of("age", -20)));
@@ -671,7 +671,7 @@ public void attributesForSets() {
671671
);
672672

673673
assertEquals("off", client.getTreatment("[email protected]", test));
674-
assertEquals("off", client.getTreatment("[email protected]", test, null));
674+
assertEquals("off", client.getTreatment("[email protected]", test, new HashMap<>()));
675675

676676
assertEquals("off", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of()));
677677
assertEquals("off", client.getTreatment("[email protected]", test, ImmutableMap.<String, Object>of("products", Lists.newArrayList())));
@@ -1894,7 +1894,7 @@ public void testTreatmentsByFlagSet() {
18941894
Map<String, String> getTreatmentResult;
18951895
for (int i = 0; i < numKeys; i++) {
18961896
String randomKey = RandomStringUtils.random(10);
1897-
getTreatmentResult = client.getTreatmentsByFlagSet(randomKey, "set1", null);
1897+
getTreatmentResult = client.getTreatmentsByFlagSet(randomKey, "set1", new HashMap<>());
18981898
assertEquals("on", getTreatmentResult.get(test));
18991899
}
19001900
verify(splitCacheConsumer, times(numKeys)).fetchMany(new ArrayList<>(Arrays.asList(test)));
@@ -1927,7 +1927,7 @@ public void testTreatmentsByFlagSetInvalid() {
19271927
new EvaluatorImp(splitCacheConsumer, segmentCacheConsumer), TELEMETRY_STORAGE, TELEMETRY_STORAGE,
19281928
flagSetsFilter
19291929
);
1930-
assertTrue(client.getTreatmentsByFlagSet(RandomStringUtils.random(10), "", null).isEmpty());
1930+
assertTrue(client.getTreatmentsByFlagSet(RandomStringUtils.random(10), "", new HashMap<>()).isEmpty());
19311931
}
19321932

19331933
@Test
@@ -1974,7 +1974,7 @@ public void testTreatmentsByFlagSets() {
19741974
Map<String, String> getTreatmentResult;
19751975
for (int i = 0; i < numKeys; i++) {
19761976
String randomKey = RandomStringUtils.random(10);
1977-
getTreatmentResult = client.getTreatmentsByFlagSets(randomKey, Arrays.asList("set1", "set3"), null);
1977+
getTreatmentResult = client.getTreatmentsByFlagSets(randomKey, Arrays.asList("set1", "set3"), new HashMap<>());
19781978
assertEquals("on", getTreatmentResult.get(test));
19791979
assertEquals("on", getTreatmentResult.get(test2));
19801980
}

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ public void getTreatmentFlagSetWithPolling() throws Exception {
773773

774774
String result = client.getTreatment("admin", "workm");
775775
Assert.assertEquals("on", result);
776-
Assert.assertEquals("on", client.getTreatmentsByFlagSet("admin", "set1", null).get("workm"));
776+
Assert.assertEquals("on", client.getTreatmentsByFlagSet("admin", "set1", new HashMap<>()).get("workm"));
777777

778778
client.destroy();
779779
splitServer.stop();
@@ -827,9 +827,9 @@ public MockResponse dispatch(RecordedRequest request) {
827827
SplitClient client = factory.client();
828828
client.blockUntilReady();
829829

830-
Assert.assertEquals("off", client.getTreatment("user1", "without_impression_toggle", null));
831-
Assert.assertEquals("off", client.getTreatment("user2", "impression_toggle_on", null));
832-
Assert.assertEquals("off", client.getTreatment("user3", "impression_toggle_off", null));
830+
Assert.assertEquals("off", client.getTreatment("user1", "without_impression_toggle", new HashMap<>()));
831+
Assert.assertEquals("off", client.getTreatment("user2", "impression_toggle_on", new HashMap<>()));
832+
Assert.assertEquals("off", client.getTreatment("user3", "impression_toggle_off", new HashMap<>()));
833833
client.destroy();
834834
boolean check1 = false, check2 = false;
835835
for (int i=0; i < allRequests.size(); i++ ) {
@@ -901,9 +901,9 @@ public MockResponse dispatch(RecordedRequest request) {
901901
SplitClient client = factory.client();
902902
client.blockUntilReady();
903903

904-
Assert.assertEquals("off", client.getTreatment("user1", "without_impression_toggle", null));
905-
Assert.assertEquals("off", client.getTreatment("user2", "impression_toggle_on", null));
906-
Assert.assertEquals("off", client.getTreatment("user3", "impression_toggle_off", null));
904+
Assert.assertEquals("off", client.getTreatment("user1", "without_impression_toggle", new HashMap<>()));
905+
Assert.assertEquals("off", client.getTreatment("user2", "impression_toggle_on", new HashMap<>()));
906+
Assert.assertEquals("off", client.getTreatment("user3", "impression_toggle_off", new HashMap<>()));
907907
client.destroy();
908908
boolean check1 = false, check2 = false, check3 = false;
909909
for (int i=0; i < allRequests.size(); i++ ) {
@@ -983,9 +983,9 @@ public MockResponse dispatch(RecordedRequest request) {
983983
SplitClient client = factory.client();
984984
client.blockUntilReady();
985985

986-
Assert.assertEquals("off", client.getTreatment("user1", "without_impression_toggle", null));
987-
Assert.assertEquals("off", client.getTreatment("user2", "impression_toggle_on", null));
988-
Assert.assertEquals("off", client.getTreatment("user3", "impression_toggle_off", null));
986+
Assert.assertEquals("off", client.getTreatment("user1", "without_impression_toggle", new HashMap<>()));
987+
Assert.assertEquals("off", client.getTreatment("user2", "impression_toggle_on", new HashMap<>()));
988+
Assert.assertEquals("off", client.getTreatment("user3", "impression_toggle_off", new HashMap<>()));
989989
client.destroy();
990990
boolean check1 = false, check2 = false, check3 = false;
991991
for (int i=0; i < allRequests.size(); i++ ) {

client/src/test/java/io/split/client/dtos/KeyImpressionTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public void TestShrinkedPropertyNames() {
2525
imp.changeNumber = 123L;
2626
imp.time = 456L;
2727
imp.previousTime = 789L;
28+
imp.properties = "{\"name\": \"value\"}";
2829

2930
String serialized = gson.toJson(imp);
3031
Map<String, Object> deSerialized = gson.fromJson(serialized, new TypeToken<Map<String, Object>>() { }.getType());
@@ -65,5 +66,11 @@ public void TestShrinkedPropertyNames() {
6566
assertThat(previousTime, is(notNullValue()));
6667
assertThat(previousTime, instanceOf(Double.class));
6768
assertThat(previousTime, is(789.0));
69+
70+
Object properties = deSerialized.get(KeyImpression.FIELD_PROPERTIES);
71+
assertThat(properties, is(notNullValue()));
72+
assertThat(properties, instanceOf(String.class));
73+
assertThat(properties, is("{\"name\": \"value\"}"));
74+
6875
}
6976
}

client/src/test/java/io/split/client/impressions/HttpImpressionsSenderTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,13 @@ public void testImpressionBulksEndpoint() throws URISyntaxException, IOException
140140

141141
// Send impressions
142142
List<TestImpressions> toSend = Arrays.asList(new TestImpressions("t1", Arrays.asList(
143-
KeyImpression.fromImpression(new Impression("k1", null, "t1", "on", 123L, "r1", 456L, null)),
144-
KeyImpression.fromImpression(new Impression("k2", null, "t1", "on", 123L, "r1", 456L, null)),
145-
KeyImpression.fromImpression(new Impression("k3", null, "t1", "on", 123L, "r1", 456L, null)))),
143+
KeyImpression.fromImpression(new Impression("k1", null, "t1", "on", 123L, "r1", 456L, null, null)),
144+
KeyImpression.fromImpression(new Impression("k2", null, "t1", "on", 123L, "r1", 456L, null, null)),
145+
KeyImpression.fromImpression(new Impression("k3", null, "t1", "on", 123L, "r1", 456L, null, null)))),
146146
new TestImpressions("t2", Arrays.asList(
147-
KeyImpression.fromImpression(new Impression("k1", null, "t2", "on", 123L, "r1", 456L, null)),
148-
KeyImpression.fromImpression(new Impression("k2", null, "t2", "on", 123L, "r1", 456L, null)),
149-
KeyImpression.fromImpression(new Impression("k3", null, "t2", "on", 123L, "r1", 456L, null)))));
147+
KeyImpression.fromImpression(new Impression("k1", null, "t2", "on", 123L, "r1", 456L, null, null)),
148+
KeyImpression.fromImpression(new Impression("k2", null, "t2", "on", 123L, "r1", 456L, null, null)),
149+
KeyImpression.fromImpression(new Impression("k3", null, "t2", "on", 123L, "r1", 456L, null, null)))));
150150
sender.postImpressionsBulk(toSend);
151151

152152
// Capture outgoing request and validate it

0 commit comments

Comments
 (0)