diff --git a/providers/flipt/README.md b/providers/flipt/README.md index 482df09af..1e95c5e99 100644 --- a/providers/flipt/README.md +++ b/providers/flipt/README.md @@ -19,7 +19,8 @@ ## Concepts * Boolean evaluation gets feature boolean evaluation / enabled status. -* Non-boolean evaluation gets feature variant key. +* Object evaluation gets variant attachment. +* Other evaluations gets feature variant key. ## Usage diff --git a/providers/flipt/pom.xml b/providers/flipt/pom.xml index 6960ddc5c..29bb603a9 100644 --- a/providers/flipt/pom.xml +++ b/providers/flipt/pom.xml @@ -20,6 +20,15 @@ false + + + markphelps + Mark Phelps + Flipt Software + https://flipt.io/ + + + io.flipt @@ -33,6 +42,12 @@ 2.0.16 + + com.fasterxml.jackson.core + jackson-databind + 2.17.2 + + com.github.tomakehurst wiremock-jre8 diff --git a/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProvider.java b/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProvider.java index 5977f9689..98ff216ad 100644 --- a/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProvider.java +++ b/providers/flipt/src/main/java/dev/openfeature/contrib/providers/flipt/FliptProvider.java @@ -40,7 +40,11 @@ public class FliptProvider extends EventProvider { @Getter private FliptClient fliptClient; - private AtomicBoolean isInitialized = new AtomicBoolean(false); + @Setter(AccessLevel.PROTECTED) + @Getter + private ProviderState state = ProviderState.NOT_READY; + + private final AtomicBoolean isInitialized = new AtomicBoolean(false); /** * Constructor. @@ -96,7 +100,8 @@ public ProviderEvaluation getBooleanEvaluation(String key, Boolean defa @Override public ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx) { - ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); + ProviderEvaluation valueProviderEvaluation = + evaluateVariant(String.class, key, new Value(defaultValue), ctx); return ProviderEvaluation.builder() .value(valueProviderEvaluation.getValue().asString()) .variant(valueProviderEvaluation.getVariant()) @@ -108,7 +113,8 @@ public ProviderEvaluation getStringEvaluation(String key, String default @Override public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { - ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); + ProviderEvaluation valueProviderEvaluation = + evaluateVariant(Integer.class, key, new Value(defaultValue), ctx); Integer value = getIntegerValue(valueProviderEvaluation, defaultValue); return ProviderEvaluation.builder() .value(value) @@ -130,7 +136,8 @@ private static Integer getIntegerValue(ProviderEvaluation valueProviderEv @Override public ProviderEvaluation getDoubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { - ProviderEvaluation valueProviderEvaluation = getObjectEvaluation(key, new Value(defaultValue), ctx); + ProviderEvaluation valueProviderEvaluation = + evaluateVariant(Double.class, key, new Value(defaultValue), ctx); Double value = getDoubleValue(valueProviderEvaluation, defaultValue); return ProviderEvaluation.builder() .value(value) @@ -152,6 +159,18 @@ private static Double getDoubleValue(ProviderEvaluation valueProviderEval @Override public ProviderEvaluation getObjectEvaluation(String key, Value defaultValue, EvaluationContext ctx) { + return evaluateVariant(Value.class, key, defaultValue, ctx); + } + + private ProviderEvaluation evaluateVariant(Class clazz, String key, Value defaultValue, + EvaluationContext ctx) { + if (!ProviderState.READY.equals(state)) { + if (ProviderState.NOT_READY.equals(state)) { + throw new ProviderNotReadyError(PROVIDER_NOT_YET_INITIALIZED); + } + throw new GeneralError(UNKNOWN_ERROR); + } + Map contextMap = ContextTransformer.transform(ctx); EvaluationRequest request = EvaluationRequest.builder().namespaceKey(fliptProviderConfig.getNamespace()) .flagKey(key).entityId(ctx.getTargetingKey()).context(contextMap).build(); @@ -172,17 +191,22 @@ public ProviderEvaluation getObjectEvaluation(String key, Value defaultVa .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()); + + if (clazz.isAssignableFrom(Value.class)) { + value = new Value(response.getVariantAttachment()); + } } return ProviderEvaluation.builder() - .value(new Value(response.getVariantKey())) - .variant(response.getVariantKey()) - .reason(TARGETING_MATCH.name()) - .flagMetadata(flagMetadataBuilder.build()) - .build(); + .value(value) + .variant(response.getVariantKey()) + .reason(TARGETING_MATCH.name()) + .flagMetadata(flagMetadataBuilder.build()) + .build(); } @Override diff --git a/providers/flipt/src/test/java/dev/openfeature/contrib/providers/flipt/FliptProviderTest.java b/providers/flipt/src/test/java/dev/openfeature/contrib/providers/flipt/FliptProviderTest.java index 6cd62eabc..fb3c3da53 100644 --- a/providers/flipt/src/test/java/dev/openfeature/contrib/providers/flipt/FliptProviderTest.java +++ b/providers/flipt/src/test/java/dev/openfeature/contrib/providers/flipt/FliptProviderTest.java @@ -14,6 +14,7 @@ import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.ProviderState; +import dev.openfeature.sdk.Value; import dev.openfeature.sdk.exceptions.GeneralError; import dev.openfeature.sdk.exceptions.ProviderNotReadyError; import lombok.SneakyThrows; @@ -31,8 +32,7 @@ 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. @@ -50,9 +50,10 @@ 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 @@ -101,7 +102,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)); } @@ -171,6 +171,24 @@ void getEvaluationMetadataTest() { assertEquals("attachment-1", flagMetadata.getString("variant-attachment")); FlagEvaluationDetails 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)); } } \ No newline at end of file diff --git a/providers/flipt/src/test/resources/variant-object.json b/providers/flipt/src/test/resources/variant-object.json new file mode 100644 index 000000000..abf764532 --- /dev/null +++ b/providers/flipt/src/test/resources/variant-object.json @@ -0,0 +1,6 @@ +{ + "flagKey": "object-flag", + "match": true, + "variantKey": "object-variant", + "variantAttachment": "{\"key1\":\"value1\",\"key2\":42,\"key3\":true}" +} \ No newline at end of file