diff --git a/providers/go-feature-flag/pom.xml b/providers/go-feature-flag/pom.xml index c1cb3458b..f24fea1e6 100644 --- a/providers/go-feature-flag/pom.xml +++ b/providers/go-feature-flag/pom.xml @@ -29,13 +29,13 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.13.4 + 2.14.0 com.fasterxml.jackson.core jackson-databind - 2.13.4 + 2.14.0 diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProvider.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProvider.java index 0423441bb..d2b09c445 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProvider.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProvider.java @@ -1,18 +1,9 @@ package dev.openfeature.contrib.providers.gofeatureflag; import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; - -import java.io.IOException; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; - import dev.openfeature.contrib.providers.gofeatureflag.bean.GoFeatureFlagRequest; import dev.openfeature.contrib.providers.gofeatureflag.bean.GoFeatureFlagResponse; import dev.openfeature.contrib.providers.gofeatureflag.bean.GoFeatureFlagUser; @@ -40,6 +31,12 @@ import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; +import java.io.IOException; +import java.time.Instant; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * GoFeatureFlagProvider is the JAVA provider implementation for the feature flag solution GO Feature Flag. @@ -120,7 +117,6 @@ public List getProviderHooks() { return FeatureProvider.super.getProviderHooks(); } - @Override public ProviderEvaluation getBooleanEvaluation( String key, Boolean defaultValue, EvaluationContext evaluationContext @@ -201,8 +197,7 @@ private ProviderEvaluation resolveEvaluationGoFeatureFlagProxy( if (Reason.DISABLED.name().equalsIgnoreCase(goffResp.getReason())) { // we don't set a variant since we are using the default value, and we are not able to know // which variant it is. - return ProviderEvaluation.builder().value(defaultValue).reason(Reason.DISABLED.toString()) - .build(); + return ProviderEvaluation.builder().value(defaultValue).reason(Reason.DISABLED.name()).build(); } if (ErrorCode.FLAG_NOT_FOUND.name().equalsIgnoreCase(goffResp.getErrorCode())) { @@ -218,6 +213,7 @@ private ProviderEvaluation resolveEvaluationGoFeatureFlagProxy( } return ProviderEvaluation.builder() + .errorCode(mapErrorCode(goffResp.getErrorCode())) .reason(goffResp.getReason()) .value(flagValue) .variant(goffResp.getVariationType()) @@ -229,6 +225,21 @@ private ProviderEvaluation resolveEvaluationGoFeatureFlagProxy( } } + /** + * mapErrorCode is mapping the errorCode in string received by the API to our internal SDK ErrorCode enum. + * + * @param errorCode - string of the errorCode received from the API + * @return an item from the enum + */ + private ErrorCode mapErrorCode(String errorCode) { + try { + return ErrorCode.valueOf(errorCode); + } catch (IllegalArgumentException e) { + return null; + } + } + + /** * convertValue is converting the object return by the proxy response in the right type. * diff --git a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagUser.java b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagUser.java index a00b9f8ed..cb16f14f3 100644 --- a/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagUser.java +++ b/providers/go-feature-flag/src/main/java/dev/openfeature/contrib/providers/gofeatureflag/bean/GoFeatureFlagUser.java @@ -31,8 +31,10 @@ public static GoFeatureFlagUser fromEvaluationContext(EvaluationContext ctx) { if (key == null || "".equals(key)) { throw new InvalidTargetingKey(); } - Value anonymousValue = ctx.getValue(anonymousFieldName); + if (anonymousValue == null) { + anonymousValue = new Value(Boolean.FALSE); + } boolean anonymous = anonymousValue.asBoolean(); Map custom = new HashMap<>(ctx.asObjectMap()); if (ctx.getValue(anonymousFieldName) != null) { diff --git a/providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java b/providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java index fcc8ec082..04db2d41d 100644 --- a/providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java +++ b/providers/go-feature-flag/src/test/java/dev/openfeature/contrib/providers/gofeatureflag/GoFeatureFlagProviderTest.java @@ -1,9 +1,5 @@ package dev.openfeature.contrib.providers.gofeatureflag; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; @@ -33,6 +29,8 @@ import okhttp3.mockwebserver.MockWebServer; import okhttp3.mockwebserver.RecordedRequest; +import static org.junit.jupiter.api.Assertions.*; + class GoFeatureFlagProviderTest { // Dispatcher is the configuration of the mock server to test the provider. final Dispatcher dispatcher = new Dispatcher() { @@ -138,15 +136,17 @@ void should_resolve_a_valid_boolean_flag_with_TARGETING_MATCH_reason() throws In GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); ProviderEvaluation res = g.getBooleanEvaluation("bool_targeting_match", false, this.evaluationContext); assertEquals(true, res.getValue()); + assertNull(res.getErrorCode()); assertEquals(Reason.TARGETING_MATCH.toString(), res.getReason()); assertEquals("True", res.getVariant()); } @Test - void should_return_unknown_reason_if_not_exists_in_SDK() throws InvalidOptions { + void should_return_custom_reason_if_returned_by_relay_proxy() throws InvalidOptions { GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); ProviderEvaluation res = g.getBooleanEvaluation("unknown_reason", false, this.evaluationContext); assertEquals(true, res.getValue()); + assertNull(res.getErrorCode()); assertEquals("CUSTOM_REASON", res.getReason()); assertEquals("True", res.getVariant()); } @@ -170,6 +170,7 @@ void should_resolve_a_valid_string_flag_with_TARGETING_MATCH_reason() throws Inv GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); ProviderEvaluation res = g.getStringEvaluation("string_key", "defaultValue", this.evaluationContext); assertEquals("CC0000", res.getValue()); + assertNull(res.getErrorCode()); assertEquals(Reason.TARGETING_MATCH.toString(), res.getReason()); assertEquals("True", res.getVariant()); } @@ -193,6 +194,7 @@ void should_resolve_a_valid_integer_flag_with_TARGETING_MATCH_reason() throws In GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); ProviderEvaluation res = g.getIntegerEvaluation("integer_key", 1200, this.evaluationContext); assertEquals(100, res.getValue()); + assertNull(res.getErrorCode()); assertEquals(Reason.TARGETING_MATCH.toString(), res.getReason()); assertEquals("True", res.getVariant()); } @@ -216,6 +218,7 @@ void should_resolve_a_valid_double_flag_with_TARGETING_MATCH_reason() throws Inv GoFeatureFlagProvider g = new GoFeatureFlagProvider(GoFeatureFlagProviderOptions.builder().endpoint(this.baseUrl.toString()).timeout(1000).build()); ProviderEvaluation res = g.getDoubleEvaluation("double_key", 1200.25, this.evaluationContext); assertEquals(100.25, res.getValue()); + assertNull(res.getErrorCode()); assertEquals(Reason.TARGETING_MATCH.toString(), res.getReason()); assertEquals("True", res.getVariant()); } @@ -234,6 +237,7 @@ void should_resolve_a_valid_value_flag_with_TARGETING_MATCH_reason() throws Inva ProviderEvaluation res = g.getObjectEvaluation("object_key", null, this.evaluationContext); Value want = new Value(new MutableStructure().add("test", "test1").add("test2", false).add("test3", 123.3).add("test4", 1)); assertEquals(want, res.getValue()); + assertNull(res.getErrorCode()); assertEquals(Reason.TARGETING_MATCH.toString(), res.getReason()); assertEquals("True", res.getVariant()); } @@ -244,6 +248,7 @@ void should_wrap_into_value_if_wrong_type() throws InvalidOptions { ProviderEvaluation res = g.getObjectEvaluation("string_key", null, this.evaluationContext); Value want = new Value("CC0000"); assertEquals(want, res.getValue()); + assertNull(res.getErrorCode()); assertEquals(Reason.TARGETING_MATCH.toString(), res.getReason()); assertEquals("True", res.getVariant()); } @@ -274,6 +279,7 @@ void should_throw_an_error_if_no_targeting_key() throws InvalidOptions { new Value("false"), new Value("test3")))); assertEquals(want, res.getValue()); + assertNull(res.getErrorCode()); assertEquals(Reason.TARGETING_MATCH.toString(), res.getReason()); assertEquals("True", res.getVariant()); }