From 9c47de02d049514c162d2f6471fc407c675847e3 Mon Sep 17 00:00:00 2001 From: lea konvalinka Date: Wed, 4 Mar 2026 15:27:34 +0100 Subject: [PATCH 1/2] feat: return code default when no default variant Signed-off-by: Konvalinka Signed-off-by: Todd Baert --- providers/flagd/spec | 2 +- .../flagd/resolver/common/ChannelBuilder.java | 5 ++ .../resolver/process/InProcessResolver.java | 10 +-- .../flagd/resolver/rpc/RpcResolver.java | 75 ++++++++++--------- .../providers/flagd/FlagdProviderTest.java | 14 ++-- .../providers/flagd/FlagdTestUtils.java | 2 +- .../providers/flagd/e2e/RunFileTest.java | 2 +- .../providers/flagd/e2e/RunInProcessTest.java | 2 +- .../providers/flagd/e2e/RunRpcTest.java | 2 +- .../process/InProcessResolverTest.java | 26 ++++++- .../flagd/resolver/process/MockEvaluator.java | 29 +++---- .../flagd/resolver/rpc/RpcResolverTest.java | 6 +- providers/flagd/test-harness | 2 +- .../contrib/tools/flagd/api/Evaluator.java | 10 +-- tools/flagd-core/schemas | 2 +- .../contrib/tools/flagd/core/FlagdCore.java | 29 +++---- .../tools/flagd/core/FlagdCoreTest.java | 17 +++-- .../flagd/core/model/FlagParserTest.java | 2 - 18 files changed, 133 insertions(+), 104 deletions(-) diff --git a/providers/flagd/spec b/providers/flagd/spec index 2d4b27b11..eefdf439c 160000 --- a/providers/flagd/spec +++ b/providers/flagd/spec @@ -1 +1 @@ -Subproject commit 2d4b27b116eaf0a53ef627987bf1a569e44ea42a +Subproject commit eefdf439c5a5b69ccde036c3c6959a4a6c17e08c diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java index 2e846adfb..1ba3ced75 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java @@ -56,6 +56,11 @@ public class ChannelBuilder { { put("service", "flagd.evaluation.v1.Service"); } + }, + new HashMap() { + { + put("service", "flagd.evaluation.v2.Service"); + } })); put("retryPolicy", new HashMap() { { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java index 7132c7614..c0a914052 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java @@ -145,35 +145,35 @@ public void shutdown() throws InterruptedException { * Resolve a boolean flag. */ public ProviderEvaluation booleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { - return evaluator.resolveBooleanValue(key, ctx); + return evaluator.resolveBooleanValue(key, defaultValue, ctx); } /** * Resolve a string flag. */ public ProviderEvaluation stringEvaluation(String key, String defaultValue, EvaluationContext ctx) { - return evaluator.resolveStringValue(key, ctx); + return evaluator.resolveStringValue(key, defaultValue, ctx); } /** * Resolve a double flag. */ public ProviderEvaluation doubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { - return evaluator.resolveDoubleValue(key, ctx); + return evaluator.resolveDoubleValue(key, defaultValue, ctx); } /** * Resolve an integer flag. */ public ProviderEvaluation integerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { - return evaluator.resolveIntegerValue(key, ctx); + return evaluator.resolveIntegerValue(key, defaultValue, ctx); } /** * Resolve an object flag. */ public ProviderEvaluation objectEvaluation(String key, Value defaultValue, EvaluationContext ctx) { - return evaluator.resolveObjectValue(key, ctx); + return evaluator.resolveObjectValue(key, defaultValue, ctx); } static QueueSource getQueueSource(final FlagdOptions options) { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/rpc/RpcResolver.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/rpc/RpcResolver.java index b09634088..17f78b906 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/rpc/RpcResolver.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/rpc/RpcResolver.java @@ -16,16 +16,16 @@ import dev.openfeature.contrib.providers.flagd.resolver.rpc.cache.Cache; import dev.openfeature.contrib.providers.flagd.resolver.rpc.strategy.ResolveFactory; import dev.openfeature.contrib.providers.flagd.resolver.rpc.strategy.ResolveStrategy; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.EventStreamRequest; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.EventStreamResponse; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveBooleanRequest; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveFloatRequest; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveIntRequest; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveObjectRequest; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveStringRequest; -import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc; -import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc.ServiceBlockingStub; -import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc.ServiceStub; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.EventStreamRequest; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.EventStreamResponse; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveBooleanRequest; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveFloatRequest; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveIntRequest; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveObjectRequest; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveStringRequest; +import dev.openfeature.flagd.grpc.evaluation.v2.ServiceGrpc; +import dev.openfeature.flagd.grpc.evaluation.v2.ServiceGrpc.ServiceBlockingStub; +import dev.openfeature.flagd.grpc.evaluation.v2.ServiceGrpc.ServiceStub; import dev.openfeature.sdk.ErrorCode; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.ImmutableMetadata; @@ -156,8 +156,7 @@ public void onError() { */ public ProviderEvaluation booleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) { ResolveBooleanRequest request = ResolveBooleanRequest.newBuilder().buildPartial(); - - return resolve(key, ctx, request, getBlockingStub()::resolveBoolean, null); + return resolve(key, defaultValue, ctx, request, getBlockingStub()::resolveBoolean, null); } /** @@ -165,7 +164,7 @@ public ProviderEvaluation booleanEvaluation(String key, Boolean default */ public ProviderEvaluation stringEvaluation(String key, String defaultValue, EvaluationContext ctx) { ResolveStringRequest request = ResolveStringRequest.newBuilder().buildPartial(); - return resolve(key, ctx, request, getBlockingStub()::resolveString, null); + return resolve(key, defaultValue, ctx, request, getBlockingStub()::resolveString, null); } /** @@ -173,39 +172,26 @@ public ProviderEvaluation stringEvaluation(String key, String defaultVal */ public ProviderEvaluation doubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) { ResolveFloatRequest request = ResolveFloatRequest.newBuilder().buildPartial(); - - return resolve(key, ctx, request, getBlockingStub()::resolveFloat, null); + return resolve(key, defaultValue, ctx, request, getBlockingStub()::resolveFloat, null); } /** * Integer evaluation from grpc resolver. */ public ProviderEvaluation integerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) { - ResolveIntRequest request = ResolveIntRequest.newBuilder().buildPartial(); - - return resolve(key, ctx, request, getBlockingStub()::resolveInt, (Object value) -> ((Long) value).intValue()); - } - - private ServiceGrpc.ServiceBlockingStub getBlockingStub() { - ServiceBlockingStub localStub = blockingStub; - - if (options.getDeadline() > 0) { - localStub = localStub.withDeadlineAfter(options.getDeadline(), TimeUnit.MILLISECONDS); - } - - return localStub; + return resolve(key, defaultValue, ctx, request, getBlockingStub()::resolveInt, (Object value) -> ((Long) value) + .intValue()); } /** * Object evaluation from grpc resolver. */ public ProviderEvaluation objectEvaluation(String key, Value defaultValue, EvaluationContext ctx) { - ResolveObjectRequest request = ResolveObjectRequest.newBuilder().buildPartial(); - return resolve( key, + defaultValue, ctx, request, getBlockingStub()::resolveObject, @@ -218,6 +204,7 @@ public ProviderEvaluation objectEvaluation(String key, Value defaultValue */ private ProviderEvaluation resolve( String key, + ValT defaultValue, EvaluationContext ctx, ReqT request, Function resolverRef, @@ -255,12 +242,18 @@ private ProviderEvaluation result = ProviderEvaluation.builder() - .value(value) - .variant(getField(response, Config.VARIANT_FIELD)) - .reason(getField(response, Config.REASON_FIELD)) - .flagMetadata(immutableMetadata) - .build(); + final String reason = getField(response, Config.REASON_FIELD); + final String variant = getField(response, Config.VARIANT_FIELD); + + final ProviderEvaluation.ProviderEvaluationBuilder resultBuilder; + if ("DEFAULT".equals(reason) && variant.isEmpty()) { + resultBuilder = ProviderEvaluation.builder().value(defaultValue); + } else { + resultBuilder = ProviderEvaluation.builder().value(value).variant(variant); + } + + ProviderEvaluation result = + resultBuilder.reason(reason).flagMetadata(immutableMetadata).build(); // cache if cache enabled if (this.isEvaluationCacheable(result)) { @@ -270,6 +263,16 @@ private ProviderEvaluation 0) { + localStub = localStub.withDeadlineAfter(options.getDeadline(), TimeUnit.MILLISECONDS); + } + + return localStub; + } + private boolean isEvaluationCacheable(ProviderEvaluation evaluation) { String reason = evaluation.getReason(); diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java index cf6cba722..86e527486 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java @@ -20,13 +20,13 @@ import dev.openfeature.contrib.providers.flagd.resolver.Resolver; import dev.openfeature.contrib.providers.flagd.resolver.common.ChannelConnector; import dev.openfeature.contrib.providers.flagd.resolver.process.InProcessResolver; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveBooleanRequest; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveBooleanResponse; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveFloatResponse; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveIntResponse; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveObjectResponse; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveStringResponse; -import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc.ServiceBlockingStub; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveBooleanRequest; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveBooleanResponse; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveFloatResponse; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveIntResponse; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveObjectResponse; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.ResolveStringResponse; +import dev.openfeature.flagd.grpc.evaluation.v2.ServiceGrpc.ServiceBlockingStub; import dev.openfeature.sdk.ErrorCode; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.FlagEvaluationDetails; diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdTestUtils.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdTestUtils.java index 1981d350a..6ae9a0ee0 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdTestUtils.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdTestUtils.java @@ -13,7 +13,7 @@ import dev.openfeature.contrib.providers.flagd.resolver.rpc.cache.Cache; import dev.openfeature.contrib.providers.flagd.resolver.rpc.cache.CacheType; import dev.openfeature.contrib.tools.flagd.core.model.FeatureFlag; -import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc; +import dev.openfeature.flagd.grpc.evaluation.v2.ServiceGrpc; import java.lang.reflect.Field; import java.util.Collections; import java.util.List; diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFileTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFileTest.java index 36180ad5d..edea71850 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFileTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunFileTest.java @@ -28,7 +28,7 @@ @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps") @ConfigurationParameter(key = OBJECT_FACTORY_PROPERTY_NAME, value = "io.cucumber.picocontainer.PicoFactory") @IncludeTags("file") -@ExcludeTags({"unixsocket", "targetURI", "reconnect", "customCert", "events", "contextEnrichment"}) +@ExcludeTags({"unixsocket", "targetURI", "reconnect", "customCert", "events", "contextEnrichment", "deprecated"}) @Testcontainers public class RunFileTest { diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunInProcessTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunInProcessTest.java index c694aa9ef..385d4e83c 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunInProcessTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunInProcessTest.java @@ -28,7 +28,7 @@ @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps") @ConfigurationParameter(key = OBJECT_FACTORY_PROPERTY_NAME, value = "io.cucumber.picocontainer.PicoFactory") @IncludeTags("in-process") -@ExcludeTags({"unixsocket"}) +@ExcludeTags({"unixsocket", "deprecated"}) @Testcontainers public class RunInProcessTest { diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunRpcTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunRpcTest.java index f0b0765fc..d98fb5986 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunRpcTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/RunRpcTest.java @@ -28,7 +28,7 @@ @ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "dev.openfeature.contrib.providers.flagd.e2e.steps") @ConfigurationParameter(key = OBJECT_FACTORY_PROPERTY_NAME, value = "io.cucumber.picocontainer.PicoFactory") @IncludeTags({"rpc"}) -@ExcludeTags({"unixsocket"}) +@ExcludeTags({"unixsocket", "deprecated"}) @Testcontainers public class RunRpcTest { diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java index 115852878..156ac2526 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java @@ -44,17 +44,31 @@ import dev.openfeature.sdk.internal.TriConsumer; import java.lang.reflect.Field; import java.time.Duration; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import org.awaitility.Awaitility; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; class InProcessResolverTest { + private final List resolversToShutdown = new ArrayList<>(); + + @AfterEach + void tearDown() throws InterruptedException { + for (InProcessResolver resolver : resolversToShutdown) { + resolver.shutdown(); + } + resolversToShutdown.clear(); + } + @Test void onError_delegatesToQueueSource() throws Exception { // given @@ -518,10 +532,13 @@ void testStateWatcherThreadIsCleanedUpDuringShutdown() throws Exception { // when inProcessResolver.init(); - Thread stateWatcher = Thread.getAllStackTraces().keySet().stream() - .filter(thread -> InProcessResolver.STATE_WATCHER_THREAD_NAME.equals(thread.getName())) - .findFirst() - .orElseThrow(); + + Field stateWatcherField = InProcessResolver.class.getDeclaredField("stateWatcher"); + stateWatcherField.setAccessible(true); + @SuppressWarnings("unchecked") + AtomicReference stateWatcherRef = (AtomicReference) stateWatcherField.get(inProcessResolver); + Thread stateWatcher = stateWatcherRef.get(); + var threadCountAfterInit = currentDaemonThreadCount(); var stateWatcherWasStarted = stateWatcher.isAlive(); inProcessResolver.shutdown(); @@ -547,6 +564,7 @@ private InProcessResolver getInProcessResolverWith( final InProcessResolver resolver = new InProcessResolver(FlagdOptions.builder().deadline(1000).build(), onConnectionEvent); + resolversToShutdown.add(resolver); return injectFlagStoreAndEvaluator(resolver, storage); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockEvaluator.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockEvaluator.java index d92c502e8..c9c704100 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockEvaluator.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/MockEvaluator.java @@ -51,28 +51,30 @@ public Map getFlagSetMetadata() { } @Override - public ProviderEvaluation resolveBooleanValue(String flagKey, EvaluationContext ctx) { - return resolve(Boolean.class, flagKey, ctx); + public ProviderEvaluation resolveBooleanValue( + String flagKey, Boolean defaultValue, EvaluationContext ctx) { + return resolve(Boolean.class, flagKey, defaultValue, ctx); } @Override - public ProviderEvaluation resolveStringValue(String flagKey, EvaluationContext ctx) { - return resolve(String.class, flagKey, ctx); + public ProviderEvaluation resolveStringValue(String flagKey, String defaultValue, EvaluationContext ctx) { + return resolve(String.class, flagKey, defaultValue, ctx); } @Override - public ProviderEvaluation resolveIntegerValue(String flagKey, EvaluationContext ctx) { - return resolve(Integer.class, flagKey, ctx); + public ProviderEvaluation resolveIntegerValue( + String flagKey, Integer defaultValue, EvaluationContext ctx) { + return resolve(Integer.class, flagKey, defaultValue, ctx); } @Override - public ProviderEvaluation resolveDoubleValue(String flagKey, EvaluationContext ctx) { - return resolve(Double.class, flagKey, ctx); + public ProviderEvaluation resolveDoubleValue(String flagKey, Double defaultValue, EvaluationContext ctx) { + return resolve(Double.class, flagKey, defaultValue, ctx); } @Override - public ProviderEvaluation resolveObjectValue(String flagKey, EvaluationContext ctx) { - final ProviderEvaluation evaluation = resolve(Object.class, flagKey, ctx); + public ProviderEvaluation resolveObjectValue(String flagKey, Value defaultValue, EvaluationContext ctx) { + final ProviderEvaluation evaluation = resolve(Object.class, flagKey, defaultValue, ctx); return ProviderEvaluation.builder() .value(Value.objectToValue(evaluation.getValue())) .variant(evaluation.getVariant()) @@ -83,7 +85,7 @@ public ProviderEvaluation resolveObjectValue(String flagKey, EvaluationCo .build(); } - private ProviderEvaluation resolve(Class type, String key, EvaluationContext ctx) { + private ProviderEvaluation resolve(Class type, String key, T defaultValue, EvaluationContext ctx) { final FeatureFlag flag = storage.getFlag(key); final Map flagSetMetadata = storage.getFlagSetMetadata(); @@ -132,9 +134,8 @@ private ProviderEvaluation resolve(Class type, String key, EvaluationC if (value == null) { if (StringUtils.isEmpty(resolvedVariant) && StringUtils.isEmpty(flag.getDefaultVariant())) { return ProviderEvaluation.builder() - .reason(Reason.ERROR.toString()) - .errorCode(ErrorCode.FLAG_NOT_FOUND) - .errorMessage("Flag '" + key + "' has no default variant defined, will use code default") + .value(defaultValue) + .reason(Reason.DEFAULT.toString()) .flagMetadata(getFlagMetadata(flagSetMetadata, flag)) .build(); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/rpc/RpcResolverTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/rpc/RpcResolverTest.java index 955d0fa2b..8e6c80498 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/rpc/RpcResolverTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/rpc/RpcResolverTest.java @@ -14,9 +14,9 @@ import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.common.ChannelConnector; import dev.openfeature.contrib.providers.flagd.resolver.common.QueueingStreamObserver; -import dev.openfeature.flagd.grpc.evaluation.Evaluation.EventStreamResponse; -import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc.ServiceBlockingStub; -import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc.ServiceStub; +import dev.openfeature.flagd.grpc.evaluation.v2.Evaluation.EventStreamResponse; +import dev.openfeature.flagd.grpc.evaluation.v2.ServiceGrpc.ServiceBlockingStub; +import dev.openfeature.flagd.grpc.evaluation.v2.ServiceGrpc.ServiceStub; import dev.openfeature.sdk.ProviderEvent; import dev.openfeature.sdk.ProviderEventDetails; import dev.openfeature.sdk.Structure; diff --git a/providers/flagd/test-harness b/providers/flagd/test-harness index b0057abde..3bff4b7ea 160000 --- a/providers/flagd/test-harness +++ b/providers/flagd/test-harness @@ -1 +1 @@ -Subproject commit b0057abde5d84272d6dd91f4737655c9d6cead15 +Subproject commit 3bff4b7eaee0efc8cfe60e0ef6fbd77441b370e6 diff --git a/tools/flagd-api/src/main/java/dev/openfeature/contrib/tools/flagd/api/Evaluator.java b/tools/flagd-api/src/main/java/dev/openfeature/contrib/tools/flagd/api/Evaluator.java index 244420d52..04f15ed52 100644 --- a/tools/flagd-api/src/main/java/dev/openfeature/contrib/tools/flagd/api/Evaluator.java +++ b/tools/flagd-api/src/main/java/dev/openfeature/contrib/tools/flagd/api/Evaluator.java @@ -45,7 +45,7 @@ public interface Evaluator { * @param ctx the evaluation context * @return the resolution result */ - ProviderEvaluation resolveBooleanValue(String flagKey, EvaluationContext ctx); + ProviderEvaluation resolveBooleanValue(String flagKey, Boolean defaultValue, EvaluationContext ctx); /** * Resolve a string flag value. @@ -54,7 +54,7 @@ public interface Evaluator { * @param ctx the evaluation context * @return the resolution result */ - ProviderEvaluation resolveStringValue(String flagKey, EvaluationContext ctx); + ProviderEvaluation resolveStringValue(String flagKey, String defaultValue, EvaluationContext ctx); /** * Resolve an integer flag value. @@ -63,7 +63,7 @@ public interface Evaluator { * @param ctx the evaluation context * @return the resolution result */ - ProviderEvaluation resolveIntegerValue(String flagKey, EvaluationContext ctx); + ProviderEvaluation resolveIntegerValue(String flagKey, Integer defaultValue, EvaluationContext ctx); /** * Resolve a double/float flag value. @@ -72,7 +72,7 @@ public interface Evaluator { * @param ctx the evaluation context * @return the resolution result */ - ProviderEvaluation resolveDoubleValue(String flagKey, EvaluationContext ctx); + ProviderEvaluation resolveDoubleValue(String flagKey, Double defaultValue, EvaluationContext ctx); /** * Resolve an object flag value. @@ -81,5 +81,5 @@ public interface Evaluator { * @param ctx the evaluation context * @return the resolution result */ - ProviderEvaluation resolveObjectValue(String flagKey, EvaluationContext ctx); + ProviderEvaluation resolveObjectValue(String flagKey, Value defaultValue, EvaluationContext ctx); } diff --git a/tools/flagd-core/schemas b/tools/flagd-core/schemas index 2852d7772..9f823b5b3 160000 --- a/tools/flagd-core/schemas +++ b/tools/flagd-core/schemas @@ -1 +1 @@ -Subproject commit 2852d7772e6b8674681a6ee6b88db10dbe3f6899 +Subproject commit 9f823b5b36bf219f8ea342de006fdf5013c1bc79 diff --git a/tools/flagd-core/src/main/java/dev/openfeature/contrib/tools/flagd/core/FlagdCore.java b/tools/flagd-core/src/main/java/dev/openfeature/contrib/tools/flagd/core/FlagdCore.java index 85d494ad4..6fb1869ad 100644 --- a/tools/flagd-core/src/main/java/dev/openfeature/contrib/tools/flagd/core/FlagdCore.java +++ b/tools/flagd-core/src/main/java/dev/openfeature/contrib/tools/flagd/core/FlagdCore.java @@ -144,28 +144,30 @@ public Map getFlagSetMetadata() { } @Override - public ProviderEvaluation resolveBooleanValue(String flagKey, EvaluationContext ctx) { - return resolve(Boolean.class, flagKey, ctx); + public ProviderEvaluation resolveBooleanValue( + String flagKey, Boolean defaultValue, EvaluationContext ctx) { + return resolve(Boolean.class, flagKey, defaultValue, ctx); } @Override - public ProviderEvaluation resolveStringValue(String flagKey, EvaluationContext ctx) { - return resolve(String.class, flagKey, ctx); + public ProviderEvaluation resolveStringValue(String flagKey, String defaultValue, EvaluationContext ctx) { + return resolve(String.class, flagKey, defaultValue, ctx); } @Override - public ProviderEvaluation resolveIntegerValue(String flagKey, EvaluationContext ctx) { - return resolve(Integer.class, flagKey, ctx); + public ProviderEvaluation resolveIntegerValue( + String flagKey, Integer defaultValue, EvaluationContext ctx) { + return resolve(Integer.class, flagKey, defaultValue, ctx); } @Override - public ProviderEvaluation resolveDoubleValue(String flagKey, EvaluationContext ctx) { - return resolve(Double.class, flagKey, ctx); + public ProviderEvaluation resolveDoubleValue(String flagKey, Double defaultValue, EvaluationContext ctx) { + return resolve(Double.class, flagKey, defaultValue, ctx); } @Override - public ProviderEvaluation resolveObjectValue(String flagKey, EvaluationContext ctx) { - final ProviderEvaluation evaluation = resolve(Object.class, flagKey, ctx); + public ProviderEvaluation resolveObjectValue(String flagKey, Value defaultValue, EvaluationContext ctx) { + final ProviderEvaluation evaluation = resolve(Object.class, flagKey, defaultValue, ctx); return ProviderEvaluation.builder() .value(Value.objectToValue(evaluation.getValue())) @@ -177,7 +179,7 @@ public ProviderEvaluation resolveObjectValue(String flagKey, EvaluationCo .build(); } - private ProviderEvaluation resolve(Class type, String key, EvaluationContext ctx) { + private ProviderEvaluation resolve(Class type, String key, T defaultValue, EvaluationContext ctx) { final FeatureFlag flag; final Map currentFlagSetMetadata; @@ -235,9 +237,8 @@ private ProviderEvaluation resolve(Class type, String key, EvaluationC if (value == null) { if (StringUtils.isEmpty(resolvedVariant) && StringUtils.isEmpty(flag.getDefaultVariant())) { return ProviderEvaluation.builder() - .reason(Reason.ERROR.toString()) - .errorCode(ErrorCode.FLAG_NOT_FOUND) - .errorMessage("Flag '" + key + "' has no default variant defined, will use code default") + .value(defaultValue) + .reason(Reason.DEFAULT.toString()) .flagMetadata(getFlagMetadata(currentFlagSetMetadata, flag)) .build(); } diff --git a/tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/FlagdCoreTest.java b/tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/FlagdCoreTest.java index c6994838d..45a1b1408 100644 --- a/tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/FlagdCoreTest.java +++ b/tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/FlagdCoreTest.java @@ -35,7 +35,7 @@ private String readResource(String path) throws IOException { @Test void resolveBooleanValue_returnsCorrectValue() { - ProviderEvaluation result = flagdCore.resolveBooleanValue("boolFlag", new ImmutableContext()); + ProviderEvaluation result = flagdCore.resolveBooleanValue("boolFlag", false, new ImmutableContext()); assertThat(result.getValue()).isTrue(); assertThat(result.getVariant()).isEqualTo("on"); @@ -44,7 +44,8 @@ void resolveBooleanValue_returnsCorrectValue() { @Test void resolveStringValue_returnsCorrectValue() { - ProviderEvaluation result = flagdCore.resolveStringValue("stringFlag", new ImmutableContext()); + ProviderEvaluation result = + flagdCore.resolveStringValue("stringFlag", "default", new ImmutableContext()); assertThat(result.getValue()).isEqualTo("hello"); assertThat(result.getVariant()).isEqualTo("greeting"); @@ -53,7 +54,7 @@ void resolveStringValue_returnsCorrectValue() { @Test void resolveIntegerValue_returnsCorrectValue() { - ProviderEvaluation result = flagdCore.resolveIntegerValue("intFlag", new ImmutableContext()); + ProviderEvaluation result = flagdCore.resolveIntegerValue("intFlag", -1, new ImmutableContext()); assertThat(result.getValue()).isEqualTo(1); assertThat(result.getVariant()).isEqualTo("one"); @@ -61,7 +62,7 @@ void resolveIntegerValue_returnsCorrectValue() { @Test void resolveDoubleValue_returnsCorrectValue() { - ProviderEvaluation result = flagdCore.resolveDoubleValue("doubleFlag", new ImmutableContext()); + ProviderEvaluation result = flagdCore.resolveDoubleValue("doubleFlag", -1.0, new ImmutableContext()); assertThat(result.getValue()).isEqualTo(3.14); assertThat(result.getVariant()).isEqualTo("pi"); @@ -69,7 +70,8 @@ void resolveDoubleValue_returnsCorrectValue() { @Test void resolveBooleanValue_flagNotFound_returnsError() { - ProviderEvaluation result = flagdCore.resolveBooleanValue("missingFlag", new ImmutableContext()); + ProviderEvaluation result = + flagdCore.resolveBooleanValue("missingFlag", false, new ImmutableContext()); assertThat(result.getErrorCode()).isNotNull(); assertThat(result.getErrorMessage()).contains("not found"); @@ -77,7 +79,8 @@ void resolveBooleanValue_flagNotFound_returnsError() { @Test void resolveBooleanValue_disabledFlag_returnsError() { - ProviderEvaluation result = flagdCore.resolveBooleanValue("disabledFlag", new ImmutableContext()); + ProviderEvaluation result = + flagdCore.resolveBooleanValue("disabledFlag", false, new ImmutableContext()); assertThat(result.getErrorCode()).isNotNull(); assertThat(result.getErrorMessage()).contains("disabled"); @@ -103,7 +106,7 @@ void setFlagsAndGetChangedKeys_returnsChangedKeys() throws FlagStoreException { void setFlagsAndGetChangedKeys_detectsRemovedFlags() throws FlagStoreException { // Given: initial config has boolFlag assertThat(flagdCore - .resolveBooleanValue("boolFlag", new ImmutableContext()) + .resolveBooleanValue("boolFlag", false, new ImmutableContext()) .getValue()) .isTrue(); diff --git a/tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/model/FlagParserTest.java b/tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/model/FlagParserTest.java index c116ee6af..fe86d3a71 100644 --- a/tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/model/FlagParserTest.java +++ b/tools/flagd-core/src/test/java/dev/openfeature/contrib/tools/flagd/core/model/FlagParserTest.java @@ -162,8 +162,6 @@ void invalidWithMulipleErrorsConfigurationsThrowsError() throws IOException { String flagString = getFlagsFromResource(INVALID_FLAG_MULTIPLE_ERRORS); assertThatThrownBy(() -> FlagParser.parseString(flagString, true)) .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("must be valid to one and only one schema") - .hasMessageContaining("$.flags.myBoolFlag: required property 'defaultVariant' not found") .hasMessageContaining("$.flags.myBoolFlag: required property 'state' not found") .hasMessageContaining( "$.flags.myBoolFlag.metadata.invalid: object found, [string, number, boolean] expected"); From e25a97d0b4e274a110e753b96ac9a81ef5fd1585 Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Mon, 9 Mar 2026 14:52:13 -0400 Subject: [PATCH 2/2] fixup: eclipse settings, double-brace init Signed-off-by: Todd Baert --- .gitignore | 4 ++++ .../flagd/resolver/common/ChannelBuilder.java | 19 ++++--------------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index bd4fcf64a..c2818f199 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,7 @@ target # IntelliJ files *.iml +# Eclipse IDE settings +.settings/ +.classpath +.project \ No newline at end of file diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java index 1ba3ced75..826ca0b7a 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/common/ChannelBuilder.java @@ -23,6 +23,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -47,21 +48,9 @@ public class ChannelBuilder { put( "name", Arrays.asList( - new HashMap() { - { - put("service", "flagd.sync.v1.FlagSyncService"); - } - }, - new HashMap() { - { - put("service", "flagd.evaluation.v1.Service"); - } - }, - new HashMap() { - { - put("service", "flagd.evaluation.v2.Service"); - } - })); + Collections.singletonMap("service", "flagd.sync.v1.FlagSyncService"), + Collections.singletonMap("service", "flagd.evaluation.v1.Service"), + Collections.singletonMap("service", "flagd.evaluation.v2.Service"))); put("retryPolicy", new HashMap() { { // 1 + 2 + 4