diff --git a/pom.xml b/pom.xml index 25104ff13..a51d074cd 100644 --- a/pom.xml +++ b/pom.xml @@ -63,6 +63,18 @@ 2.0.7 + + com.fasterxml.jackson.core + jackson-annotations + 2.15.2 + + + + com.fasterxml.jackson.core + jackson-databind + 2.15.2 + + org.mockito @@ -152,6 +164,13 @@ 4.2.0 test + + + com.fasterxml.jackson.core + jackson-core + 2.15.2 + test + diff --git a/src/main/java/dev/openfeature/sdk/ImmutableMetadata.java b/src/main/java/dev/openfeature/sdk/ImmutableMetadata.java index 1bc130b42..a9dd93fe0 100644 --- a/src/main/java/dev/openfeature/sdk/ImmutableMetadata.java +++ b/src/main/java/dev/openfeature/sdk/ImmutableMetadata.java @@ -1,5 +1,7 @@ package dev.openfeature.sdk; +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.extern.slf4j.Slf4j; import java.util.HashMap; @@ -13,10 +15,17 @@ public class ImmutableMetadata { private final Map metadata; - private ImmutableMetadata(Map metadata) { + private ImmutableMetadata(@JsonProperty("metadata") Map metadata) { this.metadata = metadata; } + // This is required for serialization support + @SuppressWarnings({"PMD.UnusedPrivateMethod"}) + @JsonGetter("metadata") + private Map getMetadata() { + return this.metadata; + } + /** * Retrieve a {@link String} value for the given key. A {@code null} value is returned if the key does not exist * or if the value is of a different type. diff --git a/src/main/java/dev/openfeature/sdk/ProviderEvaluation.java b/src/main/java/dev/openfeature/sdk/ProviderEvaluation.java index 42441a8e7..b75eae266 100644 --- a/src/main/java/dev/openfeature/sdk/ProviderEvaluation.java +++ b/src/main/java/dev/openfeature/sdk/ProviderEvaluation.java @@ -2,11 +2,14 @@ import lombok.Builder; import lombok.Data; +import lombok.extern.jackson.Jacksonized; import javax.annotation.Nullable; @SuppressWarnings("checkstyle:MissingJavadocType") -@Data @Builder +@Data +@Builder +@Jacksonized public class ProviderEvaluation implements BaseEvaluation { T value; @Nullable String variant; diff --git a/src/test/java/dev/openfeature/sdk/FlagMetadataTest.java b/src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java similarity index 54% rename from src/test/java/dev/openfeature/sdk/FlagMetadataTest.java rename to src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java index c300daa05..b91d68492 100644 --- a/src/test/java/dev/openfeature/sdk/FlagMetadataTest.java +++ b/src/test/java/dev/openfeature/sdk/ImmutableMetadataTest.java @@ -1,11 +1,13 @@ package dev.openfeature.sdk; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -class FlagMetadataTest { +class ImmutableMetadataTest { @Test @DisplayName("Test metadata payload construction and retrieval") @@ -61,4 +63,39 @@ public void notfound_error_validation() { // then assertThat(flagMetadata.getBoolean("string")).isNull(); } + + @Test + @DisplayName("Make sure class is se/deserializable") + public void test_serialize_deserialization() throws JsonProcessingException { + // given + ImmutableMetadata original = ImmutableMetadata.builder() + .addString("string", "string") + .addInteger("integer", 1) + .addLong("long", Long.MAX_VALUE) + .addFloat("float", Float.MAX_VALUE) + .addDouble("double", Double.MAX_VALUE) + .addBoolean("boolean", Boolean.FALSE) + .build(); + + + final ObjectMapper mapper = new ObjectMapper(); + + // when + final String json = mapper.writeValueAsString(original); + final ImmutableMetadata converted = mapper.readValue(json, ImmutableMetadata.class); + + // then + assertThat(json).isEqualTo("{\"metadata\":{\"boolean\":false,\"string\":\"string\"," + + "\"double\":1.7976931348623157E308,\"integer\":1,\"float\":3.4028235E38,\"long\":9223372036854775807}}"); + + assertThat(converted.getValue("string", String.class)).isEqualTo(original.getString("string")); + assertThat(converted.getValue("integer", Integer.class)).isEqualTo(original.getInteger("integer")); + assertThat(converted.getValue("boolean", Boolean.class)).isEqualTo(original.getBoolean("boolean")); + assertThat(converted.getValue("long", Long.class)).isEqualTo(original.getValue("long", Long.class)); + assertThat(converted.getValue("double", Number.class)).isEqualTo(original.getValue("double", Number.class)); + + // float get converted to double, hence representation comparison + assertThat(converted.getValue("float", Double.class).toString()) + .isEqualTo(original.getValue("float", Float.class).toString()); + } } diff --git a/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java b/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java new file mode 100644 index 000000000..5eca7be45 --- /dev/null +++ b/src/test/java/dev/openfeature/sdk/ProviderEvaluationTest.java @@ -0,0 +1,46 @@ +package dev.openfeature.sdk; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; + +import static org.assertj.core.api.Assertions.assertThat; + +class ProviderEvaluationTest { + + @Test + @DisplayName("Provider evaluation must support JSON se/deserialization") + public void test_serialization_deserialization() throws IOException { + // given + final ProviderEvaluation original = ProviderEvaluation.builder() + .reason(Reason.STATIC.toString()) + .variant("Default") + .value("StringValue") + .flagMetadata(ImmutableMetadata.builder() + .addString("key", "value") + .addInteger("integer", 10) + .build() + ) + .build(); + + + // when + final String json = new ObjectMapper().writer().writeValueAsString(original); + final ObjectMapper objectMapper = new ObjectMapper(); + final Reader reader = new StringReader(json); + final ProviderEvaluation converted = objectMapper.readValue(reader, ProviderEvaluation.class); + + // then + assertThat(converted.getReason()).isEqualTo(original.getReason()); + assertThat(converted.getVariant()).isEqualTo(original.getVariant()); + assertThat(converted.getValue()).isEqualTo(original.getValue()); + assertThat(converted.getFlagMetadata().getString("key")) + .isEqualTo(original.getFlagMetadata().getString("key")); + assertThat(converted.getFlagMetadata().getInteger("integer")) + .isEqualTo(original.getFlagMetadata().getInteger("integer")); + } +} \ No newline at end of file