diff --git a/src/main/java/com/octopus/openfeature/provider/FeatureToggleEvaluation.java b/src/main/java/com/octopus/openfeature/provider/FeatureToggleEvaluation.java index d9db7be..b43f99c 100644 --- a/src/main/java/com/octopus/openfeature/provider/FeatureToggleEvaluation.java +++ b/src/main/java/com/octopus/openfeature/provider/FeatureToggleEvaluation.java @@ -1,12 +1,15 @@ package com.octopus.openfeature.provider; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import java.util.ArrayList; import java.util.List; import java.util.Map; +@JsonIgnoreProperties(ignoreUnknown = true) class FeatureToggleEvaluation { private final String name; private final String slug; @@ -14,12 +17,16 @@ class FeatureToggleEvaluation { private final List> segments; @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - FeatureToggleEvaluation(@JsonProperty("name") String name, @JsonProperty("slug")String slug, @JsonProperty("isEnabled") boolean isEnabled, - @JsonProperty("segments") List> segments) { + FeatureToggleEvaluation( + @JsonProperty("name") String name, + @JsonProperty("slug") String slug, + @JsonProperty("isEnabled") boolean isEnabled, + @JsonDeserialize(contentUsing = SegmentDeserializer.class) @JsonProperty("segments") List> segments + ) { this.name = name; this.slug = slug; this.isEnabled = isEnabled; - + this.segments = new ArrayList<>(); if (segments != null) { this.segments.addAll(segments); diff --git a/src/main/java/com/octopus/openfeature/provider/SegmentDeserializer.java b/src/main/java/com/octopus/openfeature/provider/SegmentDeserializer.java new file mode 100644 index 0000000..3eba90d --- /dev/null +++ b/src/main/java/com/octopus/openfeature/provider/SegmentDeserializer.java @@ -0,0 +1,20 @@ +package com.octopus.openfeature.provider; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; + +import java.io.IOException; +import java.util.AbstractMap; +import java.util.Map; + +class SegmentDeserializer extends JsonDeserializer> { + @Override + public Map.Entry deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + JsonNode node = p.getCodec().readTree(p); + String key = node.get("key").asText(); + String value = node.get("value").asText(); + return new AbstractMap.SimpleEntry<>(key, value); + } +} diff --git a/src/test/java/com/octopus/openfeature/provider/FeatureToggleEvaluationDeserializationTests.java b/src/test/java/com/octopus/openfeature/provider/FeatureToggleEvaluationDeserializationTests.java new file mode 100644 index 0000000..52a514f --- /dev/null +++ b/src/test/java/com/octopus/openfeature/provider/FeatureToggleEvaluationDeserializationTests.java @@ -0,0 +1,82 @@ +package com.octopus.openfeature.provider; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class FeatureToggleEvaluationDeserializationTests { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private InputStream resource(String name) { + return getClass().getResourceAsStream(name); + } + + @Test + void shouldDeserializeEnabledToggle() throws Exception { + FeatureToggleEvaluation result = objectMapper.readValue(resource("toggle-enabled-no-segments.json"), FeatureToggleEvaluation.class); + + assertThat(result.getName()).isEqualTo("My Feature"); + assertThat(result.getSlug()).isEqualTo("my-feature"); + assertThat(result.isEnabled()).isTrue(); + assertThat(result.getSegments()).isEmpty(); + } + + @Test + void shouldDeserializeDisabledToggle() throws Exception { + FeatureToggleEvaluation result = objectMapper.readValue(resource("toggle-disabled.json"), FeatureToggleEvaluation.class); + + assertThat(result.isEnabled()).isFalse(); + } + + @Test + void shouldDeserializeToggleWithMissingSegmentsField() throws Exception { + FeatureToggleEvaluation result = objectMapper.readValue(resource("toggle-missing-segments.json"), FeatureToggleEvaluation.class); + + assertThat(result.getSegments()).isNotNull().isEmpty(); + } + + @Test + void shouldDeserializeToggleWithSegments() throws Exception { + FeatureToggleEvaluation result = objectMapper.readValue( + resource("toggle-with-segments.json"), FeatureToggleEvaluation.class); + + assertThat(result.getSegments()).hasSize(2); + assertThat(result.getSegments()).contains( + Map.entry("license-type", "free"), + Map.entry("country", "au") + ); + } + + @Test + void shouldDeserializeListOfToggles() throws Exception { + List result = objectMapper.readValue( + resource("toggle-list.json"), + new TypeReference<>() { + } + ); + + assertThat(result).hasSize(2); + assertThat(result.get(0).getSlug()).isEqualTo("feature-a"); + assertThat(result.get(0).isEnabled()).isTrue(); + assertThat(result.get(1).getSlug()).isEqualTo("feature-b"); + assertThat(result.get(1).isEnabled()).isFalse(); + } + + @Test + void shouldIgnoreExtraneousProperties() throws Exception { + FeatureToggleEvaluation result = objectMapper.readValue( + resource("toggle-with-extraneous-properties.json"), FeatureToggleEvaluation.class); + + assertThat(result.getName()).isEqualTo("My Feature"); + assertThat(result.getSlug()).isEqualTo("my-feature"); + assertThat(result.isEnabled()).isTrue(); + assertThat(result.getSegments()).contains(Map.entry("license-type", "free")); + } +} diff --git a/src/test/resources/com/octopus/openfeature/provider/toggle-disabled.json b/src/test/resources/com/octopus/openfeature/provider/toggle-disabled.json new file mode 100644 index 0000000..f09370d --- /dev/null +++ b/src/test/resources/com/octopus/openfeature/provider/toggle-disabled.json @@ -0,0 +1,6 @@ +{ + "name": "My Feature", + "slug": "my-feature", + "isEnabled": false, + "segments": null +} diff --git a/src/test/resources/com/octopus/openfeature/provider/toggle-enabled-no-segments.json b/src/test/resources/com/octopus/openfeature/provider/toggle-enabled-no-segments.json new file mode 100644 index 0000000..e9d17e9 --- /dev/null +++ b/src/test/resources/com/octopus/openfeature/provider/toggle-enabled-no-segments.json @@ -0,0 +1,6 @@ +{ + "name": "My Feature", + "slug": "my-feature", + "isEnabled": true, + "segments": null +} diff --git a/src/test/resources/com/octopus/openfeature/provider/toggle-list.json b/src/test/resources/com/octopus/openfeature/provider/toggle-list.json new file mode 100644 index 0000000..ea6d15f --- /dev/null +++ b/src/test/resources/com/octopus/openfeature/provider/toggle-list.json @@ -0,0 +1,14 @@ +[ + { + "name": "Feature A", + "slug": "feature-a", + "isEnabled": true, + "segments": null + }, + { + "name": "Feature B", + "slug": "feature-b", + "isEnabled": false, + "segments": null + } +] diff --git a/src/test/resources/com/octopus/openfeature/provider/toggle-missing-segments.json b/src/test/resources/com/octopus/openfeature/provider/toggle-missing-segments.json new file mode 100644 index 0000000..285d13e --- /dev/null +++ b/src/test/resources/com/octopus/openfeature/provider/toggle-missing-segments.json @@ -0,0 +1,5 @@ +{ + "name": "My Feature", + "slug": "my-feature", + "isEnabled": true +} diff --git a/src/test/resources/com/octopus/openfeature/provider/toggle-with-extraneous-properties.json b/src/test/resources/com/octopus/openfeature/provider/toggle-with-extraneous-properties.json new file mode 100644 index 0000000..39b78d1 --- /dev/null +++ b/src/test/resources/com/octopus/openfeature/provider/toggle-with-extraneous-properties.json @@ -0,0 +1,17 @@ +{ + "name": "My Feature", + "slug": "my-feature", + "isEnabled": true, + "segments": [ + { + "key": "license-type", + "value": "free", + "more": "data" + } + ], + "foo": "bar", + "qux": 123, + "wux": { + "nested": "value" + } +} \ No newline at end of file diff --git a/src/test/resources/com/octopus/openfeature/provider/toggle-with-segments.json b/src/test/resources/com/octopus/openfeature/provider/toggle-with-segments.json new file mode 100644 index 0000000..88ae4f0 --- /dev/null +++ b/src/test/resources/com/octopus/openfeature/provider/toggle-with-segments.json @@ -0,0 +1,15 @@ +{ + "name": "My Feature", + "slug": "my-feature", + "isEnabled": true, + "segments": [ + { + "key": "license-type", + "value": "free" + }, + { + "key": "country", + "value": "au" + } + ] +} \ No newline at end of file