From b34331a5719e2744c6addd5f78be21343dc85ff5 Mon Sep 17 00:00:00 2001 From: Justin Abrahms Date: Sat, 28 May 2022 00:39:42 -0700 Subject: [PATCH] Support for object types fixes #7 --- .../openfeature/javasdk/FeatureProvider.java | 1 + .../dev/openfeature/javasdk/Features.java | 8 ++++- .../dev/openfeature/javasdk/NoOpProvider.java | 9 ++++++ .../javasdk/OpenFeatureClient.java | 32 +++++++++++++++++++ .../javasdk/AlwaysBrokenProvider.java | 5 +++ .../javasdk/DoSomethingProvider.java | 7 ++++ .../javasdk/FlagEvaluationSpecTests.java | 12 ++----- .../openfeature/javasdk/NoOpProviderTest.java | 9 ++++-- .../javasdk/ProviderSpecTests.java | 12 +++---- 9 files changed, 76 insertions(+), 19 deletions(-) diff --git a/lib/src/main/java/dev/openfeature/javasdk/FeatureProvider.java b/lib/src/main/java/dev/openfeature/javasdk/FeatureProvider.java index bbf15f9a..eb7ebd93 100644 --- a/lib/src/main/java/dev/openfeature/javasdk/FeatureProvider.java +++ b/lib/src/main/java/dev/openfeature/javasdk/FeatureProvider.java @@ -5,4 +5,5 @@ public interface FeatureProvider { ProviderEvaluation getBooleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx, FlagEvaluationOptions options); ProviderEvaluation getStringEvaluation(String key, String defaultValue, EvaluationContext ctx, FlagEvaluationOptions options); ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options); + ProviderEvaluation getObjectEvaluation(String key, T defaultValue, EvaluationContext invocationContext, FlagEvaluationOptions options); } diff --git a/lib/src/main/java/dev/openfeature/javasdk/Features.java b/lib/src/main/java/dev/openfeature/javasdk/Features.java index ad4e0f23..beff9849 100644 --- a/lib/src/main/java/dev/openfeature/javasdk/Features.java +++ b/lib/src/main/java/dev/openfeature/javasdk/Features.java @@ -26,6 +26,12 @@ public interface Features { FlagEvaluationDetails getIntegerDetails(String key, Integer defaultValue, EvaluationContext ctx); FlagEvaluationDetails getIntegerDetails(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options); - // TODO: Object + T getObjectValue(String key, T defaultValue); + T getObjectValue(String key, T defaultValue, EvaluationContext ctx); + T getObjectValue(String key, T defaultValue, EvaluationContext ctx, FlagEvaluationOptions options); + + FlagEvaluationDetails getObjectDetails(String key, T defaultValue); + FlagEvaluationDetails getObjectDetails(String key, T defaultValue, EvaluationContext ctx); + FlagEvaluationDetails getObjectDetails(String key, T defaultValue, EvaluationContext ctx, FlagEvaluationOptions options); } diff --git a/lib/src/main/java/dev/openfeature/javasdk/NoOpProvider.java b/lib/src/main/java/dev/openfeature/javasdk/NoOpProvider.java index 81a3ad0b..7ec559e7 100644 --- a/lib/src/main/java/dev/openfeature/javasdk/NoOpProvider.java +++ b/lib/src/main/java/dev/openfeature/javasdk/NoOpProvider.java @@ -32,4 +32,13 @@ public class NoOpProvider implements FeatureProvider { .reason(Reason.DEFAULT) .build(); } + + @Override + public ProviderEvaluation getObjectEvaluation(String key, T defaultValue, EvaluationContext invocationContext, FlagEvaluationOptions options) { + return ProviderEvaluation.builder() + .value(defaultValue) + .variant("Passed in default") + .reason(Reason.DEFAULT) + .build(); + } } diff --git a/lib/src/main/java/dev/openfeature/javasdk/OpenFeatureClient.java b/lib/src/main/java/dev/openfeature/javasdk/OpenFeatureClient.java index b6073e99..04ba6291 100644 --- a/lib/src/main/java/dev/openfeature/javasdk/OpenFeatureClient.java +++ b/lib/src/main/java/dev/openfeature/javasdk/OpenFeatureClient.java @@ -69,6 +69,8 @@ public class OpenFeatureClient implements Client { providerEval = (ProviderEvaluation) provider.getStringEvaluation(key, (String) defaultValue, invocationContext, options); } else if (type == FlagValueType.INTEGER) { providerEval = (ProviderEvaluation) provider.getIntegerEvaluation(key, (Integer) defaultValue, invocationContext, options); + } else if (type == FlagValueType.OBJECT) { + providerEval = (ProviderEvaluation) provider.getObjectEvaluation(key, defaultValue, invocationContext, options); } else { throw new GeneralError("Unknown flag type"); } @@ -215,4 +217,34 @@ public class OpenFeatureClient implements Client { public FlagEvaluationDetails getIntegerDetails(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { return this.evaluateFlag(FlagValueType.INTEGER, key, defaultValue, ctx, options); } + + @Override + public T getObjectValue(String key, T defaultValue) { + return getObjectDetails(key, defaultValue).getValue(); + } + + @Override + public T getObjectValue(String key, T defaultValue, EvaluationContext ctx) { + return getObjectDetails(key, defaultValue, ctx).getValue(); + } + + @Override + public T getObjectValue(String key, T defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + return getObjectDetails(key, defaultValue, ctx, options).getValue(); + } + + @Override + public FlagEvaluationDetails getObjectDetails(String key, T defaultValue) { + return getObjectDetails(key, defaultValue, null); + } + + @Override + public FlagEvaluationDetails getObjectDetails(String key, T defaultValue, EvaluationContext ctx) { + return getObjectDetails(key, defaultValue, ctx, FlagEvaluationOptions.builder().build()); + } + + @Override + public FlagEvaluationDetails getObjectDetails(String key, T defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { + return this.evaluateFlag(FlagValueType.OBJECT, key, defaultValue, ctx, options); + } } diff --git a/lib/src/test/java/dev/openfeature/javasdk/AlwaysBrokenProvider.java b/lib/src/test/java/dev/openfeature/javasdk/AlwaysBrokenProvider.java index 4a4ff50c..39eedbb9 100644 --- a/lib/src/test/java/dev/openfeature/javasdk/AlwaysBrokenProvider.java +++ b/lib/src/test/java/dev/openfeature/javasdk/AlwaysBrokenProvider.java @@ -21,4 +21,9 @@ public class AlwaysBrokenProvider implements FeatureProvider { public ProviderEvaluation getIntegerEvaluation(String key, Integer defaultValue, EvaluationContext ctx, FlagEvaluationOptions options) { throw new NotImplementedException(); } + + @Override + public ProviderEvaluation getObjectEvaluation(String key, T defaultValue, EvaluationContext invocationContext, FlagEvaluationOptions options) { + throw new NotImplementedException(); + } } diff --git a/lib/src/test/java/dev/openfeature/javasdk/DoSomethingProvider.java b/lib/src/test/java/dev/openfeature/javasdk/DoSomethingProvider.java index 6a2dd82f..69abb347 100644 --- a/lib/src/test/java/dev/openfeature/javasdk/DoSomethingProvider.java +++ b/lib/src/test/java/dev/openfeature/javasdk/DoSomethingProvider.java @@ -25,4 +25,11 @@ public class DoSomethingProvider implements FeatureProvider { .value(defaultValue * 100) .build(); } + + @Override + public ProviderEvaluation getObjectEvaluation(String key, T defaultValue, EvaluationContext invocationContext, FlagEvaluationOptions options) { + return ProviderEvaluation.builder() + .value(null) + .build(); + } } diff --git a/lib/src/test/java/dev/openfeature/javasdk/FlagEvaluationSpecTests.java b/lib/src/test/java/dev/openfeature/javasdk/FlagEvaluationSpecTests.java index 78f479ab..514690f4 100644 --- a/lib/src/test/java/dev/openfeature/javasdk/FlagEvaluationSpecTests.java +++ b/lib/src/test/java/dev/openfeature/javasdk/FlagEvaluationSpecTests.java @@ -99,17 +99,11 @@ public class FlagEvaluationSpecTests { assertEquals(400, c.getIntegerValue(key, 4, new EvaluationContext())); assertEquals(400, c.getIntegerValue(key, 4, new EvaluationContext(), FlagEvaluationOptions.builder().build())); + assertEquals(null, c.getObjectValue(key, new Node())); + assertEquals(null, c.getObjectValue(key, new Node(), new EvaluationContext())); + assertEquals(null, c.getObjectValue(key, new Node(), new EvaluationContext(), FlagEvaluationOptions.builder().build())); } - @Specification(spec="flag evaluation", number="1.7", text="The client MUST provide methods for flag evaluation, with" + - " parameters flag key (string, required), default value (boolean | number | string | structure, required), " + - "evaluation context (optional), and evaluation options (optional), which returns the flag value.") - @Disabled - @Test void value_flags__object() { - throw new NotImplementedException(); - } - - @Specification(spec="flag evaluation", number="1.9", text="The client MUST provide methods for detailed flag value " + "evaluation with parameters flag key (string, required), default value (boolean | number | string | " + "structure, required), evaluation context (optional), and evaluation options (optional), which returns an " + diff --git a/lib/src/test/java/dev/openfeature/javasdk/NoOpProviderTest.java b/lib/src/test/java/dev/openfeature/javasdk/NoOpProviderTest.java index e4e5f869..93785612 100644 --- a/lib/src/test/java/dev/openfeature/javasdk/NoOpProviderTest.java +++ b/lib/src/test/java/dev/openfeature/javasdk/NoOpProviderTest.java @@ -1,7 +1,5 @@ package dev.openfeature.javasdk; -import dev.openfeature.javasdk.NoOpProvider; -import dev.openfeature.javasdk.ProviderEvaluation; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -25,4 +23,11 @@ public class NoOpProviderTest { ProviderEvaluation eval = p.getIntegerEvaluation("key", 4, null, null); assertEquals(4, eval.getValue()); } + + @Test void structure() { + NoOpProvider p = new NoOpProvider(); + Node node = new Node(); + ProviderEvaluation eval = p.getObjectEvaluation("key", node, null, null); + assertEquals(node, eval.getValue()); + } } diff --git a/lib/src/test/java/dev/openfeature/javasdk/ProviderSpecTests.java b/lib/src/test/java/dev/openfeature/javasdk/ProviderSpecTests.java index a5592e0c..d4921b99 100644 --- a/lib/src/test/java/dev/openfeature/javasdk/ProviderSpecTests.java +++ b/lib/src/test/java/dev/openfeature/javasdk/ProviderSpecTests.java @@ -1,6 +1,5 @@ package dev.openfeature.javasdk; -import dev.openfeature.javasdk.*; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -15,6 +14,8 @@ public class ProviderSpecTests { assertNotNull(p.getName()); } + @Specification(spec="provider", number="2.3.1", text="The feature provider interface MUST define methods for typed " + + "flag resolution, including boolean, numeric, string, and structure.") @Specification(spec="provider", number="2.4", text="In cases of normal execution, the provider MUST populate the " + "flag resolution structure's value field with the resolved flag value.") @Specification(spec="provider", number="2.2", text="The feature provider interface MUST define methods to resolve " + @@ -32,6 +33,9 @@ public class ProviderSpecTests { ProviderEvaluation boolean_result = p.getBooleanEvaluation("key", false, new EvaluationContext(), FlagEvaluationOptions.builder().build()); assertNotNull(boolean_result.getValue()); + + ProviderEvaluation> object_result = p.getObjectEvaluation("key", new Node(), new EvaluationContext(), FlagEvaluationOptions.builder().build()); + assertNotNull(boolean_result.getValue()); } @Specification(spec="provider", number="2.6", text="The provider SHOULD populate the flag resolution structure's " + @@ -48,12 +52,6 @@ public class ProviderSpecTests { assertNull(result.getErrorCode()); } - @Specification(spec="provider", number="2.3.1", text="The feature provider interface MUST define methods for typed " + - "flag resolution, including boolean, numeric, string, and structure.") - @Disabled("Don't yet support structures") @Test void structure_validation() { - - } - @Specification(spec="provider", number="2.8", text="In cases of abnormal execution, the provider MUST indicate an " + "error using the idioms of the implementation language, with an associated error code having possible " + "values PROVIDER_NOT_READY, FLAG_NOT_FOUND, PARSE_ERROR, TYPE_MISMATCH, or GENERAL.")