Skip to content

Commit

Permalink
fix(flipt): set variant attachment as value for object evaluation
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Phelps <[email protected]>
  • Loading branch information
markphelps committed Sep 17, 2024
1 parent 4f6c150 commit 7de7ec9
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 48 deletions.
17 changes: 16 additions & 1 deletion providers/flipt/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,20 @@
<description>Flipt provider for Java</description>
<url>https://www.flipt.io/</url>

<developers>
<developer>
<id>markphelps</id>
<name>Mark Phelps</name>
<organization>Flipt Software</organization>
<url>https://flipt.io/</url>
</developer>
</developers>

<dependencies>
<dependency>
<groupId>io.flipt</groupId>
<artifactId>flipt-java</artifactId>
<version>0.2.15</version>
<version>1.1.1</version>
</dependency>

<dependency>
Expand All @@ -29,6 +38,12 @@
<version>2.0.11</version>
</dependency>

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

<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package dev.openfeature.contrib.providers.flipt;

import com.flipt.api.FliptApiClient;
import com.flipt.api.resources.evaluation.types.BooleanEvaluationResponse;
import com.flipt.api.resources.evaluation.types.EvaluationRequest;
import com.flipt.api.resources.evaluation.types.VariantEvaluationResponse;
import io.flipt.api.FliptClient;
import io.flipt.api.evaluation.models.*;
import dev.openfeature.sdk.EvaluationContext;
import dev.openfeature.sdk.EventProvider;
import dev.openfeature.sdk.ImmutableMetadata;
Expand Down Expand Up @@ -41,13 +39,13 @@ public class FliptProvider extends EventProvider {

@Setter(AccessLevel.PROTECTED)
@Getter
private FliptApiClient fliptApiClient;
private FliptClient fliptClient;

@Setter(AccessLevel.PROTECTED)
@Getter
private ProviderState state = ProviderState.NOT_READY;

private AtomicBoolean isInitialized = new AtomicBoolean(false);
private final AtomicBoolean isInitialized = new AtomicBoolean(false);

/**
* Constructor.
Expand All @@ -69,7 +67,7 @@ public void initialize(EvaluationContext evaluationContext) throws Exception {
throw new GeneralError("already initialized");
}
super.initialize(evaluationContext);
fliptApiClient = fliptProviderConfig.getFliptApiClientBuilder().build();
fliptClient = fliptProviderConfig.getFliptClientBuilder().build();

state = ProviderState.READY;
log.info("finished initializing provider, state: {}", state);
Expand Down Expand Up @@ -107,14 +105,14 @@ public ProviderEvaluation<Boolean> getBooleanEvaluation(String key, Boolean defa

BooleanEvaluationResponse response = null;
try {
response = fliptApiClient.evaluation().boolean_(request);
response = fliptClient.evaluation().evaluateBoolean(request);
} catch (Exception e) {
log.error("Error evaluating boolean", e);
throw new GeneralError(e.getMessage());
}

return ProviderEvaluation.<Boolean>builder()
.value(response.getEnabled())
.value(response.isEnabled())
.reason(response.getReason().toString())
.build();
}
Expand Down Expand Up @@ -189,27 +187,29 @@ public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultVa

VariantEvaluationResponse response;
try {
response = fliptApiClient.evaluation().variant(request);
response = fliptClient.evaluation().evaluateVariant(request);
} catch (Exception e) {
log.error("Error evaluating variant", e);
throw new GeneralError(e.getMessage());
}

if (!response.getMatch()) {
if (!response.isMatch()) {
log.debug("non matching variant for {} : {}", key, response.getReason());
return ProviderEvaluation.<Value>builder()
.value(defaultValue)
.reason(DEFAULT.name())
.build();
}

Value value = new Value(response.getVariantKey());
ImmutableMetadata.ImmutableMetadataBuilder flagMetadataBuilder = ImmutableMetadata.builder();
if (response.getVariantAttachment() != null) {
if (response.getVariantAttachment() != null && !response.getVariantAttachment().isEmpty()) {
flagMetadataBuilder.addString("variant-attachment", response.getVariantAttachment());
value = new Value(response.getVariantAttachment());
}

return ProviderEvaluation.<Value>builder()
.value(new Value(response.getVariantKey()))
.value(value)
.variant(response.getVariantKey())
.reason(TARGETING_MATCH.name())
.flagMetadata(flagMetadataBuilder.build())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dev.openfeature.contrib.providers.flipt;

import com.flipt.api.FliptApiClientBuilder;
import io.flipt.api.FliptClient.FliptClientBuilder;
import lombok.Builder;
import lombok.Getter;

Expand All @@ -11,7 +11,7 @@
@Getter
@Builder
public class FliptProviderConfig {
private FliptApiClientBuilder fliptApiClientBuilder;
private FliptClientBuilder fliptClientBuilder;

@Builder.Default
private String namespace = "default";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
package dev.openfeature.contrib.providers.flipt;

import com.flipt.api.FliptApiClient;
import com.flipt.api.FliptApiClientBuilder;
import com.flipt.api.core.Environment;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfeature.sdk.*;
import io.flipt.api.FliptClient;
import io.flipt.api.FliptClient.FliptClientBuilder;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import dev.openfeature.sdk.Client;
import dev.openfeature.sdk.FlagEvaluationDetails;
import dev.openfeature.sdk.ImmutableContext;
import dev.openfeature.sdk.ImmutableMetadata;
import dev.openfeature.sdk.MutableContext;
import dev.openfeature.sdk.OpenFeatureAPI;
import dev.openfeature.sdk.ProviderEvaluation;
import dev.openfeature.sdk.ProviderEventDetails;
import dev.openfeature.sdk.ProviderState;
import dev.openfeature.sdk.exceptions.GeneralError;
import dev.openfeature.sdk.exceptions.ProviderNotReadyError;
import lombok.SneakyThrows;
Expand All @@ -26,14 +19,15 @@
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.*;

/**
* FliptProvider test, based on APIs mocking.
Expand All @@ -51,14 +45,16 @@ class FliptProviderTest {
public static final Double DOUBLE_FLAG_VALUE = 1.23;
public static final String USERS_FLAG_NAME = "users-flag";
public static final String TARGETING_KEY = "targeting_key";
public static final String OBJECT_FLAG_NAME = "object-flag";

private static FliptProvider fliptProvider;
private static Client client;

private String apiUrl;

@BeforeAll
void setUp(WireMockRuntimeInfo wmRuntimeInfo) {
apiUrl = "http://localhost:" + wmRuntimeInfo.getHttpPort() + "/";
apiUrl = "http://localhost:" + wmRuntimeInfo.getHttpPort();
fliptProvider = buildFliptProvider();
OpenFeatureAPI.getInstance().setProviderAndWait("sync", fliptProvider);
client = OpenFeatureAPI.getInstance().getClient("sync");
Expand All @@ -72,20 +68,20 @@ public void shutdown() {
private void mockFliptAPI(String url, String resourceName, String flagKey) {
stubFor(
post(urlEqualTo(url))
.withHeader("Content-Type", equalTo("application/json"))
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
.withRequestBody(WireMock.containing(flagKey))
.willReturn(
aResponse()
.withStatus(200)
.withHeader("Content-Type", "application/json")
.withHeader("Content-Type", "application/json; charset=UTF-8")
.withBody(readResourceFileContent(resourceName))));
}

@SneakyThrows
private FliptProvider buildFliptProvider() {
FliptApiClientBuilder fliptApiClientBuilder = FliptApiClient.builder().url(apiUrl).environment(Environment.custom(apiUrl));
FliptClientBuilder fliptClientBuilder = FliptClient.builder().url(apiUrl);
FliptProviderConfig fliptProviderConfig = FliptProviderConfig.builder()
.fliptApiClientBuilder(fliptApiClientBuilder)
.fliptClientBuilder(fliptClientBuilder)
.build();
return new FliptProvider(fliptProviderConfig);
}
Expand All @@ -101,7 +97,6 @@ void getBooleanEvaluation() {
mockFliptAPI("/evaluate/v1/boolean", "boolean.json", FLAG_NAME);
MutableContext evaluationContext = new MutableContext();
evaluationContext.setTargetingKey(TARGETING_KEY);
assertEquals(true, fliptProvider.getBooleanEvaluation(FLAG_NAME, false, evaluationContext).getValue());
assertEquals(true, client.getBooleanValue(FLAG_NAME, false, evaluationContext));
assertEquals(false, client.getBooleanValue("non-existing", false, evaluationContext));
}
Expand All @@ -111,8 +106,6 @@ void getStringVariantEvaluation() {
mockFliptAPI("/evaluate/v1/variant", "variant.json", VARIANT_FLAG_NAME);
MutableContext evaluationContext = new MutableContext();
evaluationContext.setTargetingKey(TARGETING_KEY);
assertEquals(VARIANT_FLAG_VALUE, fliptProvider.getStringEvaluation(VARIANT_FLAG_NAME, "",
evaluationContext).getValue());
assertEquals(VARIANT_FLAG_VALUE, client.getStringValue(VARIANT_FLAG_NAME, "", evaluationContext));
assertEquals("fallback_str", client.getStringValue("non-existing", "fallback_str", evaluationContext));
}
Expand All @@ -123,8 +116,6 @@ void getIntegerEvaluation() {
MutableContext evaluationContext = new MutableContext();
evaluationContext.setTargetingKey(TARGETING_KEY);
evaluationContext.add("userId", "int");
assertEquals(INT_FLAG_VALUE, fliptProvider.getIntegerEvaluation(INT_FLAG_NAME, 1,
evaluationContext).getValue());
assertEquals(INT_FLAG_VALUE, client.getIntegerValue(INT_FLAG_NAME, 1, evaluationContext));
assertEquals(1, client.getIntegerValue("non-existing", 1, evaluationContext));

Expand All @@ -138,8 +129,6 @@ void getDoubleEvaluation() {
MutableContext evaluationContext = new MutableContext();
evaluationContext.setTargetingKey(TARGETING_KEY);
evaluationContext.add("userId", "double");
assertEquals(DOUBLE_FLAG_VALUE, fliptProvider.getDoubleEvaluation(DOUBLE_FLAG_NAME, 1.1,
evaluationContext).getValue());
assertEquals(DOUBLE_FLAG_VALUE, client.getDoubleValue(DOUBLE_FLAG_NAME, 1.1, evaluationContext));
assertEquals(1.1, client.getDoubleValue("non-existing", 1.1, evaluationContext));

Expand All @@ -153,7 +142,6 @@ void getStringEvaluationByUser() {
MutableContext evaluationContext = new MutableContext();
evaluationContext.setTargetingKey(TARGETING_KEY);
evaluationContext.add("userId", "111");
assertEquals(VARIANT_FLAG_VALUE, fliptProvider.getStringEvaluation(USERS_FLAG_NAME, "", evaluationContext).getValue());
assertEquals(VARIANT_FLAG_VALUE, client.getStringValue(USERS_FLAG_NAME, "", evaluationContext));
evaluationContext.add("userId", "2");
assertEquals("", client.getStringValue(USERS_FLAG_NAME, "", evaluationContext));
Expand All @@ -164,12 +152,26 @@ void getEvaluationMetadataTest() {
mockFliptAPI("/evaluate/v1/variant", "variant.json", VARIANT_FLAG_NAME);
MutableContext evaluationContext = new MutableContext();
evaluationContext.setTargetingKey(TARGETING_KEY);
ProviderEvaluation<String> stringEvaluation = fliptProvider.getStringEvaluation(VARIANT_FLAG_NAME, "",
evaluationContext);
ImmutableMetadata flagMetadata = stringEvaluation.getFlagMetadata();
assertEquals("attachment-1", flagMetadata.getString("variant-attachment"));
FlagEvaluationDetails<String> nonExistingFlagEvaluation = client.getStringDetails("non-existing", "", evaluationContext);
assertEquals(null, nonExistingFlagEvaluation.getFlagMetadata().getBoolean("variant-attachment"));
assertNull(nonExistingFlagEvaluation.getFlagMetadata().getBoolean("variant-attachment"));
}

@SneakyThrows
@Test
void getObjectEvaluationTest() {
mockFliptAPI("/evaluate/v1/variant", "variant-object.json", OBJECT_FLAG_NAME);
MutableContext evaluationContext = new MutableContext();
evaluationContext.setTargetingKey(TARGETING_KEY);
evaluationContext.add("userId", "object");

Value expectedValue = new Value("{\"key1\":\"value1\",\"key2\":42,\"key3\":true}");
Value emptyValue = new Value();

assertEquals(expectedValue, client.getObjectValue(OBJECT_FLAG_NAME, emptyValue, evaluationContext));
assertEquals(emptyValue, client.getObjectValue("non-existing", emptyValue, evaluationContext));

// non-object flag value
assertEquals(emptyValue, client.getObjectValue(VARIANT_FLAG_NAME, emptyValue, evaluationContext));
}

@SneakyThrows
Expand Down
6 changes: 6 additions & 0 deletions providers/flipt/src/test/resources/variant-object.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"flagKey": "object-flag",
"match": true,
"variantKey": "object-variant",
"variantAttachment": "{\"key1\":\"value1\",\"key2\":42,\"key3\":true}"
}

0 comments on commit 7de7ec9

Please sign in to comment.