Skip to content

Commit

Permalink
fix: GO Feature Flag provider fix issues + compatible with SDK 0.1.0 (#…
Browse files Browse the repository at this point in the history
…132)

* Fix CVE-2022-42003 on jackson

Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>

* #128 - Fix NPE when anonymous field is missing

Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>

* Change to be compatible with SDK 0.1.0

Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>

* revert previous change

Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>

Signed-off-by: Thomas Poignant <thomas.poignant@gofeatureflag.org>
  • Loading branch information
thomaspoignant authored Nov 17, 2022
1 parent 590a046 commit 046947d
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 20 deletions.
4 changes: 2 additions & 2 deletions providers/go-feature-flag/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.4</version>
<version>2.14.0</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4</version>
<version>2.14.0</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -120,7 +117,6 @@ public List<Hook> getProviderHooks() {
return FeatureProvider.super.getProviderHooks();
}


@Override
public ProviderEvaluation<Boolean> getBooleanEvaluation(
String key, Boolean defaultValue, EvaluationContext evaluationContext
Expand Down Expand Up @@ -201,8 +197,7 @@ private <T> ProviderEvaluation<T> 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.<T>builder().value(defaultValue).reason(Reason.DISABLED.toString())
.build();
return ProviderEvaluation.<T>builder().value(defaultValue).reason(Reason.DISABLED.name()).build();
}

if (ErrorCode.FLAG_NOT_FOUND.name().equalsIgnoreCase(goffResp.getErrorCode())) {
Expand All @@ -218,6 +213,7 @@ private <T> ProviderEvaluation<T> resolveEvaluationGoFeatureFlagProxy(
}

return ProviderEvaluation.<T>builder()
.errorCode(mapErrorCode(goffResp.getErrorCode()))
.reason(goffResp.getReason())
.value(flagValue)
.variant(goffResp.getVariationType())
Expand All @@ -229,6 +225,21 @@ private <T> ProviderEvaluation<T> 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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, Object> custom = new HashMap<>(ctx.asObjectMap());
if (ctx.getValue(anonymousFieldName) != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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<Boolean> 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<Boolean> res = g.getBooleanEvaluation("unknown_reason", false, this.evaluationContext);
assertEquals(true, res.getValue());
assertNull(res.getErrorCode());
assertEquals("CUSTOM_REASON", res.getReason());
assertEquals("True", res.getVariant());
}
Expand All @@ -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<String> 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());
}
Expand All @@ -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<Integer> 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());
}
Expand All @@ -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<Double> 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());
}
Expand All @@ -234,6 +237,7 @@ void should_resolve_a_valid_value_flag_with_TARGETING_MATCH_reason() throws Inva
ProviderEvaluation<Value> 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());
}
Expand All @@ -244,6 +248,7 @@ void should_wrap_into_value_if_wrong_type() throws InvalidOptions {
ProviderEvaluation<Value> 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());
}
Expand Down Expand Up @@ -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());
}
Expand Down

0 comments on commit 046947d

Please sign in to comment.