Skip to content

Commit 8a8ed69

Browse files
bug fixes for Structure and better tests
1 parent a61face commit 8a8ed69

File tree

2 files changed

+85
-17
lines changed

2 files changed

+85
-17
lines changed

src/main/java/io/split/openfeature/SplitProvider.java

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
import io.split.client.SplitClient;
1515
import io.split.openfeature.utils.Serialization;
1616

17-
import java.util.HashMap;
17+
import java.time.ZonedDateTime;
18+
import java.time.format.DateTimeParseException;
1819
import java.util.List;
1920
import java.util.Map;
2021
import java.util.stream.Collectors;
@@ -127,11 +128,8 @@ public ProviderEvaluation<Structure> getObjectEvaluation(String key, Structure d
127128
if (noTreatment(evaluated)) {
128129
return constructProviderEvaluation(defaultTreatment, evaluated, Reason.DEFAULT, ErrorCode.FLAG_NOT_FOUND.name());
129130
}
130-
Structure structure = new Structure();
131131
Map<String, Object> rawMap = Serialization.stringToMap(evaluated);
132-
for (Map.Entry<String, Object> rawEntry : rawMap.entrySet()) {
133-
structure.add(rawEntry.getKey(),(String) rawEntry.getValue());
134-
}
132+
Structure structure = mapToStructure(rawMap);
135133
return constructProviderEvaluation(structure, evaluated);
136134
} catch (OpenFeatureError e) {
137135
throw e;
@@ -173,17 +171,12 @@ private <T> ProviderEvaluation<T> constructProviderEvaluation(T value, String va
173171
}
174172

175173
private Map<String, Object> getMapFromStructMap(Map<String, Value> structMap) {
176-
Map<String, Object> toReturn = new HashMap<>();
177-
for (Map.Entry<String, Value> entry : structMap.entrySet()) {
178-
String key = entry.getKey();
179-
Value value = entry.getValue();
180-
toReturn.put(key, getInnerValue(value));
181-
}
182-
return toReturn;
174+
return structMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> getInnerValue(e.getValue())));
183175
}
184176

185-
private Map<String, Object> getMapFromStructure(Structure structure) {
186-
return getMapFromStructMap(structure.asMap());
177+
private Structure mapToStructure(Map<String, Object> map) {
178+
return new Structure(
179+
map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e -> objectToValue(e.getValue()))));
187180
}
188181

189182
private Object getInnerValue(Value value) {
@@ -205,19 +198,49 @@ private Object getInnerValue(Value value) {
205198
}
206199
object = value.asZonedDateTime();
207200
if (object != null) {
208-
return object.toString();
201+
return object;
209202
}
210203
object = value.asStructure();
211204
if (object != null) {
212205
// must return a map
213-
return getMapFromStructure((Structure) object);
206+
return getMapFromStructMap(((Structure) object).asMap());
214207
}
215208
object = value.asList();
216209
if (object != null) {
217210
// must return a list of inner objects
218211
List<Value> values = (List<Value>) object;
219212
return values.stream().map(this::getInnerValue).collect(Collectors.toList());
220213
}
221-
return null;
214+
throw new ClassCastException("Could not get inner value from Value object.");
215+
}
216+
217+
private Value objectToValue(Object object) {
218+
if (object instanceof Value) {
219+
return (Value) object;
220+
} else if (object instanceof String) {
221+
// try to parse to zoned date time, otherwise use as string
222+
try {
223+
return new Value(ZonedDateTime.parse((String) object));
224+
} catch (DateTimeParseException e) {
225+
return new Value((String) object);
226+
}
227+
} else if (object instanceof Boolean) {
228+
return new Value((Boolean) object);
229+
} else if (object instanceof Integer) {
230+
return new Value((Integer) object);
231+
} else if (object instanceof Double) {
232+
return new Value((Double) object);
233+
} else if (object instanceof Structure) {
234+
return new Value((Structure) object);
235+
} else if (object instanceof List) {
236+
// need to translate each elem in list to a value
237+
return new Value(((List<Object>) object).stream().map(this::objectToValue).collect(Collectors.toList()));
238+
} else if (object instanceof ZonedDateTime) {
239+
return new Value((ZonedDateTime) object);
240+
} else if (object instanceof Map) {
241+
return new Value(mapToStructure((Map<String, Object>) object));
242+
} else {
243+
throw new ClassCastException("Could not cast Object to Value");
244+
}
222245
}
223246
}

src/test/java/io/split/openfeature/SplitProviderTest.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
import org.mockito.Mock;
1414
import org.mockito.MockitoAnnotations;
1515

16+
import java.time.ZoneId;
17+
import java.time.ZonedDateTime;
18+
import java.util.List;
1619
import java.util.Map;
1720

1821
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -400,6 +403,48 @@ public void evalStructureRegularTest() {
400403
assertEquals(treatment, response.getValue());
401404
}
402405

406+
@Test
407+
public void evalStructureComplexTest() {
408+
// an object treatment should eval to that object
409+
SplitProvider splitProvider = new SplitProvider(mockSplitClient);
410+
411+
String flagName = "flagName";
412+
ZonedDateTime zonedDateTime = ZonedDateTime.of(2020, 1, 10, 0, 0, 0, 0, ZoneId.of("UTC"));
413+
Structure treatment = new Structure(Map.of(
414+
"string", new Value("blah"),
415+
"int", new Value(10),
416+
"double", new Value(100D),
417+
"bool", new Value(true),
418+
"struct", new Value(
419+
new Structure(Map.of(
420+
"foo", new Value("bar"),
421+
"baz", new Value(10),
422+
"innerMap", new Value(
423+
new Structure(Map.of(
424+
"aa", new Value("bb"))))))),
425+
"list", new Value(
426+
List.of(
427+
new Value(1),
428+
new Value(true),
429+
new Value(
430+
new Structure(Map.of(
431+
"cc", new Value("dd")
432+
))),
433+
new Value(
434+
new Structure(Map.of(
435+
"ee", new Value(1)
436+
))))),
437+
"dateTime", new Value(zonedDateTime)
438+
));
439+
String treatmentAsString = "{\"string\":\"blah\",\"int\":10,\"double\":100.0,\"bool\":true, \"struct\":{\"foo\":\"bar\",\"baz\":10,\"innerMap\":{\"aa\":\"bb\"}},\"list\":[1,true,{\"cc\":\"dd\"},{\"ee\":1}],\"dateTime\":\"2020-01-10T00:00Z[UTC]\"}";
440+
441+
when(mockSplitClient.getTreatment(eq(key), eq(flagName), anyMap())).thenReturn(treatmentAsString);
442+
443+
ProviderEvaluation<Structure> response =
444+
splitProvider.getObjectEvaluation(flagName, new Structure(Map.of("foo", new Value("bar"))), evaluationContext);
445+
assertEquals(treatment, response.getValue());
446+
}
447+
403448
@Test
404449
public void evalStructureErrorTest() {
405450
// a treatment that can not be converted to the required object should throw an error

0 commit comments

Comments
 (0)