Skip to content

Commit 1a3952f

Browse files
authored
Handle legacy SplitChange DTO (#761)
1 parent a516caf commit 1a3952f

5 files changed

Lines changed: 429 additions & 10 deletions

File tree

src/main/java/io/split/android/client/dtos/TargetingRulesChange.java

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

3-
import androidx.annotation.VisibleForTesting;
4-
53
import com.google.gson.annotations.SerializedName;
64

75
public class TargetingRulesChange {
@@ -19,12 +17,10 @@ public RuleBasedSegmentChange getRuleBasedSegmentsChange() {
1917
return rbs;
2018
}
2119

22-
@VisibleForTesting
2320
public static TargetingRulesChange create(SplitChange splitChange) {
2421
return create(splitChange, RuleBasedSegmentChange.createEmpty());
2522
}
2623

27-
@VisibleForTesting
2824
public static TargetingRulesChange create(SplitChange splitChange, RuleBasedSegmentChange ruleBasedSegmentChange) {
2925
TargetingRulesChange targetingRulesChange = new TargetingRulesChange();
3026
targetingRulesChange.ff = splitChange;
Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
package io.split.android.client.service.rules;
22

3-
import com.google.gson.JsonSyntaxException;
3+
import com.google.gson.stream.JsonReader;
4+
import com.google.gson.stream.JsonToken;
45

6+
import java.io.StringReader;
7+
8+
import io.split.android.client.dtos.SplitChange;
59
import io.split.android.client.dtos.TargetingRulesChange;
610
import io.split.android.client.service.http.HttpResponseParser;
711
import io.split.android.client.service.http.HttpResponseParserException;
@@ -12,12 +16,54 @@ public class TargetingRulesResponseParser implements HttpResponseParser<Targetin
1216
@Override
1317
public TargetingRulesChange parse(String responseData) throws HttpResponseParserException {
1418
try {
15-
return Json.fromJson(responseData, TargetingRulesChange.class);
16-
// return TargetingRulesChange.create(Json.fromJson(responseData, SplitChange.class));
17-
} catch (JsonSyntaxException e) {
18-
throw new HttpResponseParserException("Syntax error parsing my segments http response: " + e.getLocalizedMessage());
19+
if (responseData == null || responseData.isEmpty()) {
20+
return null;
21+
}
22+
23+
if (isNewDto(responseData)) {
24+
// New DTO: parse as TargetingRulesChange
25+
return Json.fromJson(responseData, TargetingRulesChange.class);
26+
} else {
27+
// Legacy DTO: parse as SplitChange, wrap as TargetingRulesChange
28+
SplitChange splitChange = Json.fromJson(responseData, SplitChange.class);
29+
if (splitChange == null) {
30+
return null;
31+
}
32+
return TargetingRulesChange.create(splitChange);
33+
}
1934
} catch (Exception e) {
20-
throw new HttpResponseParserException("Unknown error parsing my segments http response: " + e.getLocalizedMessage());
35+
throw new HttpResponseParserException("Error parsing splitChanges http response: " + e.getLocalizedMessage());
36+
}
37+
}
38+
39+
private boolean isNewDto(String json) throws Exception {
40+
try (JsonReader reader = new JsonReader(new StringReader(json))) {
41+
reader.setLenient(true);
42+
if (reader.peek() == JsonToken.BEGIN_OBJECT) {
43+
reader.beginObject();
44+
if (reader.hasNext()) {
45+
String name = reader.nextName();
46+
if (newFieldNameIsPresent(name)) {
47+
return true;
48+
}
49+
50+
reader.skipValue();
51+
while (reader.hasNext()) {
52+
name = reader.nextName();
53+
if (newFieldNameIsPresent(name)) {
54+
return true;
55+
}
56+
reader.skipValue();
57+
}
58+
}
59+
} else {
60+
throw new HttpResponseParserException("Error parsing splitChanges http response: not a JSON object");
61+
}
62+
return false;
2163
}
2264
}
65+
66+
private static boolean newFieldNameIsPresent(String name) {
67+
return "ff".equals(name) || "rbs".equals(name);
68+
}
2369
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,65 @@
11
package io.split.android.client.service.rules;
22

3+
import static org.junit.Assert.assertEquals;
4+
import static org.junit.Assert.assertNotNull;
5+
import static org.junit.Assert.assertNull;
6+
import static org.junit.Assert.assertTrue;
7+
8+
import org.junit.Before;
9+
import org.junit.Test;
10+
11+
import io.split.android.client.dtos.TargetingRulesChange;
12+
import io.split.android.client.service.http.HttpResponseParserException;
13+
import io.split.android.helpers.FileHelper;
14+
315
public class TargetingRulesResponseParserTest {
416

17+
private TargetingRulesResponseParser parser;
18+
private FileHelper fileHelper;
19+
20+
@Before
21+
public void setUp() {
22+
parser = new TargetingRulesResponseParser();
23+
fileHelper = new FileHelper();
24+
}
25+
26+
@Test
27+
public void parsesNewTargetingRulesChangeJson() throws Exception {
28+
String json = fileHelper.loadFileContent("split_changes_small.json");
29+
TargetingRulesChange result = parser.parse(json);
30+
assertNotNull(result);
31+
assertNotNull(result.getFeatureFlagsChange());
32+
assertEquals("FACUNDO_TEST", result.getFeatureFlagsChange().splits.get(0).name);
33+
assertEquals(1506703262916L, result.getFeatureFlagsChange().till);
34+
assertEquals(-1, result.getFeatureFlagsChange().since);
35+
assertEquals(1506703262920L, result.getRuleBasedSegmentsChange().getSince());
36+
assertEquals(1506703263000L, result.getRuleBasedSegmentsChange().getTill());
37+
assertEquals("mauro_rule_based_segment", result.getRuleBasedSegmentsChange().getSegments().get(0).getName());
38+
}
39+
40+
@Test
41+
public void parsesLegacySplitChangeJson() throws Exception {
42+
String json = fileHelper.loadFileContent("split_changes_legacy.json");
43+
TargetingRulesChange result = parser.parse(json);
44+
assertNotNull(result);
45+
assertNotNull(result.getFeatureFlagsChange());
46+
assertEquals("FACUNDO_TEST", result.getFeatureFlagsChange().splits.get(0).name);
47+
assertEquals(1506703262916L, result.getFeatureFlagsChange().till);
48+
assertEquals(-1, result.getFeatureFlagsChange().since);
49+
assertEquals(-1, result.getRuleBasedSegmentsChange().getSince());
50+
assertEquals(-1, result.getRuleBasedSegmentsChange().getTill());
51+
assertTrue(result.getRuleBasedSegmentsChange().getSegments().isEmpty());
52+
}
53+
54+
@Test
55+
public void parseNullReturnsNull() throws HttpResponseParserException {
56+
TargetingRulesChange result = parser.parse(null);
57+
assertNull(result);
58+
}
59+
60+
@Test
61+
public void parseEmptyReturnsNull() throws HttpResponseParserException {
62+
TargetingRulesChange result = parser.parse("");
63+
assertNull(result);
64+
}
565
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
{
2+
"splits": [
3+
{
4+
"trafficTypeName": "account",
5+
"name": "FACUNDO_TEST",
6+
"trafficAllocation": 59,
7+
"trafficAllocationSeed": -2108186082,
8+
"seed": -1947050785,
9+
"status": "ACTIVE",
10+
"killed": false,
11+
"defaultTreatment": "off",
12+
"changeNumber": 1506703262916,
13+
"algo": 2,
14+
"conditions": [
15+
{
16+
"conditionType": "WHITELIST",
17+
"matcherGroup": {
18+
"combiner": "AND",
19+
"matchers": [
20+
{
21+
"keySelector": null,
22+
"matcherType": "WHITELIST",
23+
"negate": false,
24+
"userDefinedSegmentMatcherData": null,
25+
"whitelistMatcherData": {
26+
"whitelist": [
27+
"nico_test",
28+
"othertest"
29+
]
30+
},
31+
"unaryNumericMatcherData": null,
32+
"betweenMatcherData": null,
33+
"booleanMatcherData": null,
34+
"dependencyMatcherData": null,
35+
"stringMatcherData": null
36+
}
37+
]
38+
},
39+
"partitions": [
40+
{
41+
"treatment": "on",
42+
"size": 100
43+
}
44+
],
45+
"label": "whitelisted"
46+
},
47+
{
48+
"conditionType": "WHITELIST",
49+
"matcherGroup": {
50+
"combiner": "AND",
51+
"matchers": [
52+
{
53+
"keySelector": null,
54+
"matcherType": "WHITELIST",
55+
"negate": false,
56+
"userDefinedSegmentMatcherData": null,
57+
"whitelistMatcherData": {
58+
"whitelist": [
59+
"bla"
60+
]
61+
},
62+
"unaryNumericMatcherData": null,
63+
"betweenMatcherData": null,
64+
"booleanMatcherData": null,
65+
"dependencyMatcherData": null,
66+
"stringMatcherData": null
67+
}
68+
]
69+
},
70+
"partitions": [
71+
{
72+
"treatment": "off",
73+
"size": 100
74+
}
75+
],
76+
"label": "whitelisted"
77+
},
78+
{
79+
"conditionType": "ROLLOUT",
80+
"matcherGroup": {
81+
"combiner": "AND",
82+
"matchers": [
83+
{
84+
"keySelector": {
85+
"trafficType": "account",
86+
"attribute": null
87+
},
88+
"matcherType": "ALL_KEYS",
89+
"negate": false,
90+
"userDefinedSegmentMatcherData": null,
91+
"whitelistMatcherData": null,
92+
"unaryNumericMatcherData": null,
93+
"betweenMatcherData": null,
94+
"booleanMatcherData": null,
95+
"dependencyMatcherData": null,
96+
"stringMatcherData": null
97+
}
98+
]
99+
},
100+
"partitions": [
101+
{
102+
"treatment": "on",
103+
"size": 0
104+
},
105+
{
106+
"treatment": "off",
107+
"size": 100
108+
},
109+
{
110+
"treatment": "visa",
111+
"size": 0
112+
}
113+
],
114+
"label": "in segment all"
115+
}
116+
]
117+
}
118+
],
119+
"since": -1,
120+
"till": 1506703262916
121+
}

0 commit comments

Comments
 (0)