Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ target

# IntelliJ files
*.iml
# Eclipse IDE settings
.settings/
.classpath
.project
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -47,16 +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");
}
}));
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,35 +145,35 @@ public void shutdown() throws InterruptedException {
* Resolve a boolean flag.
*/
public ProviderEvaluation<Boolean> booleanEvaluation(String key, Boolean defaultValue, EvaluationContext ctx) {
return evaluator.resolveBooleanValue(key, ctx);
return evaluator.resolveBooleanValue(key, defaultValue, ctx);
}

/**
* Resolve a string flag.
*/
public ProviderEvaluation<String> stringEvaluation(String key, String defaultValue, EvaluationContext ctx) {
return evaluator.resolveStringValue(key, ctx);
return evaluator.resolveStringValue(key, defaultValue, ctx);
}

/**
* Resolve a double flag.
*/
public ProviderEvaluation<Double> doubleEvaluation(String key, Double defaultValue, EvaluationContext ctx) {
return evaluator.resolveDoubleValue(key, ctx);
return evaluator.resolveDoubleValue(key, defaultValue, ctx);
}

/**
* Resolve an integer flag.
*/
public ProviderEvaluation<Integer> integerEvaluation(String key, Integer defaultValue, EvaluationContext ctx) {
return evaluator.resolveIntegerValue(key, ctx);
return evaluator.resolveIntegerValue(key, defaultValue, ctx);
}

/**
* Resolve an object flag.
*/
public ProviderEvaluation<Value> objectEvaluation(String key, Value defaultValue, EvaluationContext ctx) {
return evaluator.resolveObjectValue(key, ctx);
return evaluator.resolveObjectValue(key, defaultValue, ctx);
}

static QueueSource getQueueSource(final FlagdOptions options) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -156,56 +156,42 @@ public void onError() {
*/
public ProviderEvaluation<Boolean> 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);
}

/**
* String evaluation from grpc resolver.
*/
public ProviderEvaluation<String> 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);
}

/**
* Double evaluation from grpc resolver.
*/
public ProviderEvaluation<Double> 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<Integer> 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<Value> objectEvaluation(String key, Value defaultValue, EvaluationContext ctx) {

ResolveObjectRequest request = ResolveObjectRequest.newBuilder().buildPartial();

return resolve(
key,
defaultValue,
ctx,
request,
getBlockingStub()::resolveObject,
Expand All @@ -218,6 +204,7 @@ public ProviderEvaluation<Value> objectEvaluation(String key, Value defaultValue
*/
private <ValT, ReqT extends Message, ResT extends Message> ProviderEvaluation<ValT> resolve(
String key,
ValT defaultValue,
EvaluationContext ctx,
ReqT request,
Function<ReqT, ResT> resolverRef,
Expand Down Expand Up @@ -255,12 +242,18 @@ private <ValT, ReqT extends Message, ResT extends Message> ProviderEvaluation<Va
// Extract metadata from response
ImmutableMetadata immutableMetadata = metadataFromResponse(response);

ProviderEvaluation<ValT> result = ProviderEvaluation.<ValT>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<ValT> resultBuilder;
if ("DEFAULT".equals(reason) && variant.isEmpty()) {
resultBuilder = ProviderEvaluation.<ValT>builder().value(defaultValue);
} else {
resultBuilder = ProviderEvaluation.<ValT>builder().value(value).variant(variant);
}

ProviderEvaluation<ValT> result =
resultBuilder.reason(reason).flagMetadata(immutableMetadata).build();

// cache if cache enabled
if (this.isEvaluationCacheable(result)) {
Expand All @@ -270,6 +263,16 @@ private <ValT, ReqT extends Message, ResT extends Message> ProviderEvaluation<Va
return result;
}

private ServiceGrpc.ServiceBlockingStub getBlockingStub() {
ServiceBlockingStub localStub = blockingStub;

if (options.getDeadline() > 0) {
localStub = localStub.withDeadlineAfter(options.getDeadline(), TimeUnit.MILLISECONDS);
}

return localStub;
}

private <T> boolean isEvaluationCacheable(ProviderEvaluation<T> evaluation) {
String reason = evaluation.getReason();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<InProcessResolver> resolversToShutdown = new ArrayList<>();

@AfterEach
void tearDown() throws InterruptedException {
for (InProcessResolver resolver : resolversToShutdown) {
resolver.shutdown();
}
resolversToShutdown.clear();
}

@Test
void onError_delegatesToQueueSource() throws Exception {
// given
Expand Down Expand Up @@ -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<Thread> stateWatcherRef = (AtomicReference<Thread>) stateWatcherField.get(inProcessResolver);
Thread stateWatcher = stateWatcherRef.get();

var threadCountAfterInit = currentDaemonThreadCount();
var stateWatcherWasStarted = stateWatcher.isAlive();
inProcessResolver.shutdown();
Expand All @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,28 +51,30 @@ public Map<String, Object> getFlagSetMetadata() {
}

@Override
public ProviderEvaluation<Boolean> resolveBooleanValue(String flagKey, EvaluationContext ctx) {
return resolve(Boolean.class, flagKey, ctx);
public ProviderEvaluation<Boolean> resolveBooleanValue(
String flagKey, Boolean defaultValue, EvaluationContext ctx) {
return resolve(Boolean.class, flagKey, defaultValue, ctx);
}

@Override
public ProviderEvaluation<String> resolveStringValue(String flagKey, EvaluationContext ctx) {
return resolve(String.class, flagKey, ctx);
public ProviderEvaluation<String> resolveStringValue(String flagKey, String defaultValue, EvaluationContext ctx) {
return resolve(String.class, flagKey, defaultValue, ctx);
}

@Override
public ProviderEvaluation<Integer> resolveIntegerValue(String flagKey, EvaluationContext ctx) {
return resolve(Integer.class, flagKey, ctx);
public ProviderEvaluation<Integer> resolveIntegerValue(
String flagKey, Integer defaultValue, EvaluationContext ctx) {
return resolve(Integer.class, flagKey, defaultValue, ctx);
}

@Override
public ProviderEvaluation<Double> resolveDoubleValue(String flagKey, EvaluationContext ctx) {
return resolve(Double.class, flagKey, ctx);
public ProviderEvaluation<Double> resolveDoubleValue(String flagKey, Double defaultValue, EvaluationContext ctx) {
return resolve(Double.class, flagKey, defaultValue, ctx);
}

@Override
public ProviderEvaluation<Value> resolveObjectValue(String flagKey, EvaluationContext ctx) {
final ProviderEvaluation<Object> evaluation = resolve(Object.class, flagKey, ctx);
public ProviderEvaluation<Value> resolveObjectValue(String flagKey, Value defaultValue, EvaluationContext ctx) {
final ProviderEvaluation<Object> evaluation = resolve(Object.class, flagKey, defaultValue, ctx);
return ProviderEvaluation.<Value>builder()
.value(Value.objectToValue(evaluation.getValue()))
.variant(evaluation.getVariant())
Expand All @@ -83,7 +85,7 @@ public ProviderEvaluation<Value> resolveObjectValue(String flagKey, EvaluationCo
.build();
}

private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationContext ctx) {
private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, T defaultValue, EvaluationContext ctx) {
final FeatureFlag flag = storage.getFlag(key);
final Map<String, Object> flagSetMetadata = storage.getFlagSetMetadata();

Expand Down Expand Up @@ -132,9 +134,8 @@ private <T> ProviderEvaluation<T> resolve(Class<T> type, String key, EvaluationC
if (value == null) {
if (StringUtils.isEmpty(resolvedVariant) && StringUtils.isEmpty(flag.getDefaultVariant())) {
return ProviderEvaluation.<T>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();
}
Expand Down
Loading
Loading