From dd43b0a014d86aeef536d426f1fcd88eb137803a Mon Sep 17 00:00:00 2001 From: Kavindu Dodanduwa Date: Tue, 16 Jan 2024 14:54:21 -0800 Subject: [PATCH] add scope to in-process evaluations Signed-off-by: Kavindu Dodanduwa --- .../resolver/process/InProcessResolver.java | 14 ++- .../process/InProcessResolverTest.java | 98 +++++++++++++------ 2 files changed, 79 insertions(+), 33 deletions(-) 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 cc11e2774..76a7367a4 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 @@ -13,6 +13,7 @@ import dev.openfeature.contrib.providers.flagd.resolver.process.targeting.Operator; import dev.openfeature.contrib.providers.flagd.resolver.process.targeting.TargetingRuleException; import dev.openfeature.sdk.EvaluationContext; +import dev.openfeature.sdk.ImmutableMetadata; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.ProviderState; import dev.openfeature.sdk.Reason; @@ -38,6 +39,7 @@ public class InProcessResolver implements Resolver { private final Consumer stateConsumer; private final Operator operator; private final long deadline; + private final ImmutableMetadata metadata; private final AtomicBoolean connected = new AtomicBoolean(false); /** @@ -52,6 +54,10 @@ public InProcessResolver(FlagdOptions options, Consumer stateCons this.deadline = options.getDeadline(); this.stateConsumer = stateConsumer; this.operator = new Operator(); + this.metadata = options.getSelector() == null ? null : + ImmutableMetadata.builder() + .addString("scope", options.getSelector()) + .build(); } /** @@ -204,10 +210,12 @@ private ProviderEvaluation resolve(Class type, String key, throw new TypeMismatchError(message); } - return ProviderEvaluation.builder() + final ProviderEvaluation.ProviderEvaluationBuilder evaluationBuilder = ProviderEvaluation.builder() .value((T) value) .variant(resolvedVariant) - .reason(reason) - .build(); + .reason(reason); + + return this.metadata == null ? evaluationBuilder.build() : + evaluationBuilder.flagMetadata(this.metadata).build(); } } 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 2d46c9490..743672763 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 @@ -1,34 +1,10 @@ package dev.openfeature.contrib.providers.flagd.resolver.process; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.BOOLEAN_FLAG; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.DISABLED_FLAG; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.DOUBLE_FLAG; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_IF_IN_TARGET; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_INVALID_TARGET; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_SHORTHAND_TARGETING; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.INT_FLAG; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.OBJECT_FLAG; -import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.VARIANT_MISMATCH_FLAG; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; - -import java.lang.reflect.Field; -import java.time.Duration; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - import dev.openfeature.contrib.providers.flagd.FlagdOptions; import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag; import dev.openfeature.contrib.providers.flagd.resolver.process.storage.StorageState; import dev.openfeature.sdk.ImmutableContext; +import dev.openfeature.sdk.ImmutableMetadata; import dev.openfeature.sdk.MutableContext; import dev.openfeature.sdk.ProviderEvaluation; import dev.openfeature.sdk.ProviderState; @@ -37,6 +13,31 @@ import dev.openfeature.sdk.exceptions.FlagNotFoundError; import dev.openfeature.sdk.exceptions.ParseError; import dev.openfeature.sdk.exceptions.TypeMismatchError; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.BOOLEAN_FLAG; +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.DISABLED_FLAG; +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.DOUBLE_FLAG; +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_IF_IN_TARGET; +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_INVALID_TARGET; +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_SHORTHAND_TARGETING; +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.INT_FLAG; +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.OBJECT_FLAG; +import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.VARIANT_MISMATCH_FLAG; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; class InProcessResolverTest { @@ -330,13 +331,50 @@ public void targetingErrorEvaluationFlag() throws Exception { }); } - private InProcessResolver getInProcessResolverWth(final MockStorage storage, Consumer stateConsumer) + @Test + public void validateMetadataInEvaluationResult() throws Exception { + // given + final String scope = "appName=myApp"; + final Map flagMap = new HashMap<>(); + flagMap.put("booleanFlag", BOOLEAN_FLAG); + + InProcessResolver inProcessResolver = getInProcessResolverWth( + FlagdOptions.builder().selector(scope).build(), + new MockStorage(flagMap)); + + // when + ProviderEvaluation providerEvaluation = inProcessResolver.booleanEvaluation("booleanFlag", false, + new ImmutableContext()); + + // then + final ImmutableMetadata metadata = providerEvaluation.getFlagMetadata(); + assertNotNull(metadata); + assertEquals(scope, metadata.getString("scope")); + } + + private InProcessResolver getInProcessResolverWth(final FlagdOptions options, final MockStorage storage) throws NoSuchFieldException, IllegalAccessException { - Field flagStore = InProcessResolver.class.getDeclaredField("flagStore"); - flagStore.setAccessible(true); - InProcessResolver resolver = new InProcessResolver(FlagdOptions.builder().deadline(1000).build(), - stateConsumer); + final InProcessResolver resolver = new InProcessResolver(options, providerState -> {}); + return injectFlagStore(resolver, storage); + } + + + private InProcessResolver getInProcessResolverWth(final MockStorage storage, + final Consumer stateConsumer) + throws NoSuchFieldException, IllegalAccessException { + + final InProcessResolver resolver = new InProcessResolver( + FlagdOptions.builder().deadline(1000).build(), stateConsumer); + return injectFlagStore(resolver, storage); + } + + // helper to inject flagStore override + private InProcessResolver injectFlagStore(final InProcessResolver resolver, final MockStorage storage) + throws NoSuchFieldException, IllegalAccessException { + + final Field flagStore = InProcessResolver.class.getDeclaredField("flagStore"); + flagStore.setAccessible(true); flagStore.set(resolver, storage); return resolver;