Skip to content

Commit b3873ae

Browse files
feat: flagd add scope to in-process evaluations (#637)
Signed-off-by: Kavindu Dodanduwa <[email protected]>
1 parent 7874c5c commit b3873ae

File tree

2 files changed

+79
-33
lines changed

2 files changed

+79
-33
lines changed

Diff for: providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolver.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import dev.openfeature.contrib.providers.flagd.resolver.process.targeting.Operator;
1414
import dev.openfeature.contrib.providers.flagd.resolver.process.targeting.TargetingRuleException;
1515
import dev.openfeature.sdk.EvaluationContext;
16+
import dev.openfeature.sdk.ImmutableMetadata;
1617
import dev.openfeature.sdk.ProviderEvaluation;
1718
import dev.openfeature.sdk.ProviderState;
1819
import dev.openfeature.sdk.Reason;
@@ -38,6 +39,7 @@ public class InProcessResolver implements Resolver {
3839
private final Consumer<ProviderState> stateConsumer;
3940
private final Operator operator;
4041
private final long deadline;
42+
private final ImmutableMetadata metadata;
4143
private final AtomicBoolean connected = new AtomicBoolean(false);
4244

4345
/**
@@ -52,6 +54,10 @@ public InProcessResolver(FlagdOptions options, Consumer<ProviderState> stateCons
5254
this.deadline = options.getDeadline();
5355
this.stateConsumer = stateConsumer;
5456
this.operator = new Operator();
57+
this.metadata = options.getSelector() == null ? null :
58+
ImmutableMetadata.builder()
59+
.addString("scope", options.getSelector())
60+
.build();
5561
}
5662

5763
/**
@@ -204,10 +210,12 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key,
204210
throw new TypeMismatchError(message);
205211
}
206212

207-
return ProviderEvaluation.<T>builder()
213+
final ProviderEvaluation.ProviderEvaluationBuilder<T> evaluationBuilder = ProviderEvaluation.<T>builder()
208214
.value((T) value)
209215
.variant(resolvedVariant)
210-
.reason(reason)
211-
.build();
216+
.reason(reason);
217+
218+
return this.metadata == null ? evaluationBuilder.build() :
219+
evaluationBuilder.flagMetadata(this.metadata).build();
212220
}
213221
}

Diff for: providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/process/InProcessResolverTest.java

+68-30
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,10 @@
11
package dev.openfeature.contrib.providers.flagd.resolver.process;
22

3-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.BOOLEAN_FLAG;
4-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.DISABLED_FLAG;
5-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.DOUBLE_FLAG;
6-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_IF_IN_TARGET;
7-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_INVALID_TARGET;
8-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_SHORTHAND_TARGETING;
9-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.INT_FLAG;
10-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.OBJECT_FLAG;
11-
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.VARIANT_MISMATCH_FLAG;
12-
import static org.junit.jupiter.api.Assertions.assertEquals;
13-
import static org.junit.jupiter.api.Assertions.assertThrows;
14-
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
15-
16-
import java.lang.reflect.Field;
17-
import java.time.Duration;
18-
import java.util.HashMap;
19-
import java.util.Map;
20-
import java.util.concurrent.BlockingQueue;
21-
import java.util.concurrent.LinkedBlockingQueue;
22-
import java.util.concurrent.TimeUnit;
23-
import java.util.function.Consumer;
24-
25-
import org.junit.jupiter.api.Assertions;
26-
import org.junit.jupiter.api.Test;
27-
283
import dev.openfeature.contrib.providers.flagd.FlagdOptions;
294
import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag;
305
import dev.openfeature.contrib.providers.flagd.resolver.process.storage.StorageState;
316
import dev.openfeature.sdk.ImmutableContext;
7+
import dev.openfeature.sdk.ImmutableMetadata;
328
import dev.openfeature.sdk.MutableContext;
339
import dev.openfeature.sdk.ProviderEvaluation;
3410
import dev.openfeature.sdk.ProviderState;
@@ -37,6 +13,31 @@
3713
import dev.openfeature.sdk.exceptions.FlagNotFoundError;
3814
import dev.openfeature.sdk.exceptions.ParseError;
3915
import dev.openfeature.sdk.exceptions.TypeMismatchError;
16+
import org.junit.jupiter.api.Assertions;
17+
import org.junit.jupiter.api.Test;
18+
19+
import java.lang.reflect.Field;
20+
import java.time.Duration;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
import java.util.concurrent.BlockingQueue;
24+
import java.util.concurrent.LinkedBlockingQueue;
25+
import java.util.concurrent.TimeUnit;
26+
import java.util.function.Consumer;
27+
28+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.BOOLEAN_FLAG;
29+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.DISABLED_FLAG;
30+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.DOUBLE_FLAG;
31+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_IF_IN_TARGET;
32+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_INVALID_TARGET;
33+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.FLAG_WIH_SHORTHAND_TARGETING;
34+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.INT_FLAG;
35+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.OBJECT_FLAG;
36+
import static dev.openfeature.contrib.providers.flagd.resolver.process.MockFlags.VARIANT_MISMATCH_FLAG;
37+
import static org.junit.jupiter.api.Assertions.assertEquals;
38+
import static org.junit.jupiter.api.Assertions.assertNotNull;
39+
import static org.junit.jupiter.api.Assertions.assertThrows;
40+
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
4041

4142
class InProcessResolverTest {
4243

@@ -330,13 +331,50 @@ public void targetingErrorEvaluationFlag() throws Exception {
330331
});
331332
}
332333

333-
private InProcessResolver getInProcessResolverWth(final MockStorage storage, Consumer<ProviderState> stateConsumer)
334+
@Test
335+
public void validateMetadataInEvaluationResult() throws Exception {
336+
// given
337+
final String scope = "appName=myApp";
338+
final Map<String, FeatureFlag> flagMap = new HashMap<>();
339+
flagMap.put("booleanFlag", BOOLEAN_FLAG);
340+
341+
InProcessResolver inProcessResolver = getInProcessResolverWth(
342+
FlagdOptions.builder().selector(scope).build(),
343+
new MockStorage(flagMap));
344+
345+
// when
346+
ProviderEvaluation<Boolean> providerEvaluation = inProcessResolver.booleanEvaluation("booleanFlag", false,
347+
new ImmutableContext());
348+
349+
// then
350+
final ImmutableMetadata metadata = providerEvaluation.getFlagMetadata();
351+
assertNotNull(metadata);
352+
assertEquals(scope, metadata.getString("scope"));
353+
}
354+
355+
private InProcessResolver getInProcessResolverWth(final FlagdOptions options, final MockStorage storage)
334356
throws NoSuchFieldException, IllegalAccessException {
335-
Field flagStore = InProcessResolver.class.getDeclaredField("flagStore");
336-
flagStore.setAccessible(true);
337357

338-
InProcessResolver resolver = new InProcessResolver(FlagdOptions.builder().deadline(1000).build(),
339-
stateConsumer);
358+
final InProcessResolver resolver = new InProcessResolver(options, providerState -> {});
359+
return injectFlagStore(resolver, storage);
360+
}
361+
362+
363+
private InProcessResolver getInProcessResolverWth(final MockStorage storage,
364+
final Consumer<ProviderState> stateConsumer)
365+
throws NoSuchFieldException, IllegalAccessException {
366+
367+
final InProcessResolver resolver = new InProcessResolver(
368+
FlagdOptions.builder().deadline(1000).build(), stateConsumer);
369+
return injectFlagStore(resolver, storage);
370+
}
371+
372+
// helper to inject flagStore override
373+
private InProcessResolver injectFlagStore(final InProcessResolver resolver, final MockStorage storage)
374+
throws NoSuchFieldException, IllegalAccessException {
375+
376+
final Field flagStore = InProcessResolver.class.getDeclaredField("flagStore");
377+
flagStore.setAccessible(true);
340378
flagStore.set(resolver, storage);
341379

342380
return resolver;

0 commit comments

Comments
 (0)