Skip to content

Commit

Permalink
refactor: Add ConfidenceWrapper for OF
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziodemaria committed Jun 20, 2024
1 parent accfb22 commit 7c98857
Show file tree
Hide file tree
Showing 21 changed files with 148 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
import dev.openfeature.sdk.exceptions.FlagNotFoundError;
import dev.openfeature.sdk.exceptions.GeneralError;
import dev.openfeature.sdk.exceptions.TypeMismatchError;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Status.Code;
import io.grpc.StatusRuntimeException;
import java.util.Map;
Expand All @@ -27,63 +25,18 @@
/** OpenFeature Provider for feature flagging with the Confidence platform */
public class ConfidenceFeatureProvider implements FeatureProvider {

private final Confidence confidence;

/**
* ConfidenceFeatureProvider constructor
*
* @param confidence an instance of the Confidence
*/
public ConfidenceFeatureProvider(Confidence confidence) {
this.confidence = confidence;
}
private final FlagReaderForProvider confidence;

private static final Logger log =
org.slf4j.LoggerFactory.getLogger(ConfidenceFeatureProvider.class);

/**
* ConfidenceFeatureProvider constructor
*
* @param clientSecret generated from the Confidence portal
* @param managedChannel gRPC channel
* @deprecated This constructor is deprecated. Please use {@link
* #ConfidenceFeatureProvider(Confidence)} instead.
*/
@Deprecated()
public ConfidenceFeatureProvider(String clientSecret, ManagedChannel managedChannel) {
this(Confidence.builder(clientSecret).flagResolverManagedChannel(managedChannel).build());
}

/**
* ConfidenceFeatureProvider constructor
*
* @param clientSecret generated from the Confidence portal
* @deprecated This constructor is deprecated. Please use {@link
* #ConfidenceFeatureProvider(Confidence)} instead.
*/
@Deprecated()
public ConfidenceFeatureProvider(String clientSecret) {
this(clientSecret, ManagedChannelBuilder.forAddress("edge-grpc.spotify.com", 443).build());
}

/**
* ConfidenceFeatureProvider constructor that allows you to override the default gRPC host and
* port, used for local resolver.
*
* @param clientSecret generated from the Confidence portal
* @param host gRPC host you want to connect to.
* @param port port of the gRPC host that you want to use.
* @deprecated This constructor is deprecated. Please use {@link
* #ConfidenceFeatureProvider(Confidence)} instead.
*/
@Deprecated()
public ConfidenceFeatureProvider(String clientSecret, String host, int port) {
this(clientSecret, ManagedChannelBuilder.forAddress(host, port).usePlaintext().build());
public ConfidenceFeatureProvider(FlagReaderForProvider confidence) {
this.confidence = confidence;
}

@Override
public Metadata getMetadata() {
return () -> "com.spotify.confidence.flags.resolver.v1.FlagResolverService";
return () -> "ConfidenceProvider";
}

@Override
Expand Down Expand Up @@ -163,7 +116,7 @@ public ProviderEvaluation<Value> getObjectEvaluation(
Map.of(
OPEN_FEATURE_RESOLVE_CONTEXT_KEY,
ConfidenceValue.Struct.fromProto(evaluationContext)))
.resolveFlags(requestFlagName, true)
.resolveFlags(requestFlagName, "SDK_ID_JAVA_PROVIDER")
.get();

if (resolveFlagResponse.getResolvedFlagsList().isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.spotify.confidence;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.junit.jupiter.api.Assertions.*;

import dev.openfeature.sdk.ErrorCode;
Expand All @@ -16,20 +16,21 @@

class ConfidenceFeatureProviderTest {

private Confidence root;
private FlagReaderForProvider root;
private FakeEventSenderEngine fakeEngine;
private ResolverClientTestUtils.FakeFlagResolverClient fakeFlagResolverClient;

@BeforeEach
public void setup() {
fakeEngine = new FakeEventSenderEngine(new FakeClock());
fakeFlagResolverClient = new ResolverClientTestUtils.FakeFlagResolverClient();
root = Confidence.create(fakeEngine, fakeFlagResolverClient);
root = Confidence.createForProvider(fakeEngine, fakeFlagResolverClient);
}

@Test
public void testCloseChildShouldReturnDefaultsFromOpenFeatureApi() throws IOException {
final Confidence child = root.withContext(Map.of("child-key", ConfidenceValue.of("child")));
final FlagReaderForProvider child =
root.withContext(Map.of("child-key", ConfidenceValue.of("child")));
OpenFeatureAPI.getInstance().setProvider(new ConfidenceFeatureProvider(child));
child.close();
final boolean defaultValue = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import com.google.protobuf.util.Structs;
import com.google.protobuf.util.Values;
import com.spotify.confidence.Confidence.ConfidenceMetadata;
import com.spotify.confidence.ResolverClientTestUtils.ValueSchemaHolder;
import com.spotify.confidence.shaded.flags.resolver.v1.FlagResolverServiceGrpc.FlagResolverServiceImplBase;
import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsRequest;
Expand Down Expand Up @@ -70,8 +71,10 @@ void beforeEach() {
final FakeEventSenderEngine fakeEventSender = new FakeEventSenderEngine(new FakeClock());
channel = InProcessChannelBuilder.forName(serverName).directExecutor().build();
final FlagResolverClientImpl flagResolver =
new FlagResolverClientImpl(new GrpcFlagResolver("fake-secret", channel));
final Confidence confidence = Confidence.create(fakeEventSender, flagResolver);
new FlagResolverClientImpl(
new GrpcFlagResolver("fake-secret", channel, new ConfidenceMetadata("")));
final FlagReaderForProvider confidence =
Confidence.createForProvider(fakeEventSender, flagResolver);
final FeatureProvider featureProvider = new ConfidenceFeatureProvider(confidence);

openFeatureAPI = OpenFeatureAPI.getInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
public class FlagResolverContextTest {
private FakeFlagResolver fakeFlagResolver;
private Client client;
private Confidence confidence;
private FlagReaderForProvider confidence;

@BeforeEach
void beforeEach() {
final FakeEventSenderEngine fakeEventSender = new FakeEventSenderEngine(new FakeClock());
this.fakeFlagResolver = new FakeFlagResolver();
final FlagResolverClientImpl flagResolver = new FlagResolverClientImpl(fakeFlagResolver);
this.confidence = Confidence.create(fakeEventSender, flagResolver);
this.confidence = Confidence.createForProvider(fakeEventSender, flagResolver);
final FeatureProvider featureProvider = new ConfidenceFeatureProvider(confidence);

final OpenFeatureAPI openFeatureAPI = OpenFeatureAPI.getInstance();
Expand Down Expand Up @@ -50,7 +50,7 @@ public void close() {}

@Override
public CompletableFuture<ResolveFlagsResponse> resolve(
String flag, Struct context, Boolean isProvider) {
String flag, Struct context, String providerId) {
this.context = context;
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static class FakeFlagResolverClient implements FlagResolverClient {

@Override
public CompletableFuture<ResolveFlagsResponse> resolveFlags(
String flag, ConfidenceValue.Struct context, Boolean isProvider) {
String flag, ConfidenceValue.Struct context, String providerId) {
resolves.put(flag, context);
return CompletableFuture.completedFuture(response);
}
Expand Down
44 changes: 33 additions & 11 deletions sdk-java/src/main/java/com/spotify/confidence/Confidence.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.google.common.collect.Maps;
import com.google.common.io.Closer;
import com.spotify.confidence.ConfidenceUtils.FlagPath;
import com.spotify.confidence.ConfidenceValue.Struct;
import com.spotify.confidence.Exceptions.IllegalValuePath;
import com.spotify.confidence.Exceptions.IllegalValueType;
import com.spotify.confidence.Exceptions.IncompatibleValueType;
Expand All @@ -34,8 +35,7 @@
import org.slf4j.Logger;

@Beta
public abstract class Confidence implements EventSender, Closeable {

public abstract class Confidence implements ConfidenceInstance, FlagReaderForProvider {
protected Map<String, ConfidenceValue> context = Maps.newHashMap();
private static final Logger log = org.slf4j.LoggerFactory.getLogger(Confidence.class);

Expand Down Expand Up @@ -120,7 +120,8 @@ public <T> FlagEvaluation<T> getEvaluation(String key, T defaultValue) {
try {
final FlagPath flagPath = getPath(key);
final String requestFlagName = "flags/" + flagPath.getFlag();
final ResolveFlagsResponse response = resolveFlags(requestFlagName, false).get();
final ResolveFlagsResponse response =
resolveFlags(requestFlagName, "SDK_ID_JAVA_CONFIDENCE").get();
if (response.getResolvedFlagsList().isEmpty()) {
final String errorMessage =
String.format("No active flag '%s' was found", flagPath.getFlag());
Expand Down Expand Up @@ -176,19 +177,25 @@ public <T> FlagEvaluation<T> getEvaluation(String key, T defaultValue) {
}
}

CompletableFuture<ResolveFlagsResponse> resolveFlags(String flagName, Boolean isProvider) {
return client().resolveFlags(flagName, getContext(), isProvider);
public CompletableFuture<ResolveFlagsResponse> resolveFlags(String flagName, String providerId) {
return client().resolveFlags(flagName, getContext(), providerId);
}

@VisibleForTesting
static Confidence create(
protected static ConfidenceInstance create(
EventSenderEngine eventSenderEngine, FlagResolverClient flagResolverClient) {
final Closer closer = Closer.create();
closer.register(eventSenderEngine);
closer.register(flagResolverClient);
return new RootInstance(new ClientDelegate(closer, flagResolverClient, eventSenderEngine));
}

@VisibleForTesting
protected static FlagReaderForProvider createForProvider(
EventSenderEngine eventSenderEngine, FlagResolverClient flagResolverClient) {
return (FlagReaderForProvider) create(eventSenderEngine, flagResolverClient);
}

public static Confidence.Builder builder(String clientSecret) {
return new Confidence.Builder(clientSecret);
}
Expand Down Expand Up @@ -220,8 +227,8 @@ public void flush() {

@Override
public CompletableFuture<ResolveFlagsResponse> resolveFlags(
String flag, ConfidenceValue.Struct context, Boolean isProvider) {
return flagResolverClient.resolveFlags(flag, context, isProvider);
String flag, Struct context, String providerId) {
return flagResolverClient.resolveFlags(flag, context, providerId);
}

@Override
Expand Down Expand Up @@ -301,6 +308,7 @@ public static class Builder {
.keepAliveTime(Duration.ofMinutes(5).getSeconds(), TimeUnit.SECONDS)
.build();
private ManagedChannel flagResolverManagedChannel = DEFAULT_CHANNEL;
private ConfidenceMetadata metadata = new ConfidenceMetadata("SDK_ID_JAVA_CONFIDENCE");

public Builder(@Nonnull String clientSecret) {
this.clientSecret = clientSecret;
Expand All @@ -319,17 +327,21 @@ public Builder flagResolverManagedChannel(ManagedChannel managedChannel) {
return this;
}

public Confidence build() {
public ConfidenceInstance build() {
final FlagResolverClient flagResolverClient =
new FlagResolverClientImpl(
new GrpcFlagResolver(clientSecret, flagResolverManagedChannel));
new GrpcFlagResolver(clientSecret, flagResolverManagedChannel, metadata));
final EventSenderEngine eventSenderEngine =
new EventSenderEngineImpl(clientSecret, DEFAULT_CHANNEL, Instant::now);
new EventSenderEngineImpl(clientSecret, DEFAULT_CHANNEL, Instant::now, metadata);
closer.register(flagResolverClient);
closer.register(eventSenderEngine);
return new RootInstance(new ClientDelegate(closer, flagResolverClient, eventSenderEngine));
}

public FlagReaderForProvider buildForProvider() {
return (FlagReaderForProvider) build();
}

private void registerChannelForShutdown(ManagedChannel channel) {
this.closer.register(
() -> {
Expand All @@ -343,4 +355,14 @@ private void registerChannelForShutdown(ManagedChannel channel) {
});
}
}

public static class ConfidenceMetadata {
String sdkId;
String sdkVersion;

public ConfidenceMetadata(String sdkId) {
this.sdkId = sdkId;
this.sdkVersion = ConfidenceUtils.getSdkVersion();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.spotify.confidence;

public interface ConfidenceInstance extends FlagReader, EventSender {}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package com.spotify.confidence;

import com.google.common.annotations.Beta;
import java.io.Closeable;
import java.util.Map;

@Beta
public interface EventSender extends Contextual {
public interface EventSender extends Contextual, Closeable {
public void track(String eventName, ConfidenceValue.Struct data);

public void track(String eventName);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.spotify.confidence;

import com.google.common.annotations.VisibleForTesting;
import com.spotify.confidence.Confidence.ConfidenceMetadata;
import com.spotify.confidence.events.v1.Event;
import dev.failsafe.Failsafe;
import dev.failsafe.FailsafeExecutor;
Expand All @@ -17,7 +18,6 @@
import org.slf4j.Logger;

class EventSenderEngineImpl implements EventSenderEngine {

static final String EVENT_NAME_PREFIX = "eventDefinitions/";
static final int DEFAULT_BATCH_SIZE = 25;
static final Duration DEFAULT_MAX_FLUSH_INTERVAL = Duration.ofSeconds(60);
Expand Down Expand Up @@ -64,10 +64,11 @@ class EventSenderEngineImpl implements EventSenderEngine {
pollingThread.start();
}

EventSenderEngineImpl(String clientSecret, ManagedChannel channel, Clock clock) {
EventSenderEngineImpl(
String clientSecret, ManagedChannel channel, Clock clock, ConfidenceMetadata metadata) {
this(
DEFAULT_BATCH_SIZE,
new GrpcEventUploader(clientSecret, clock, channel),
new GrpcEventUploader(clientSecret, clock, channel, metadata),
clock,
DEFAULT_MAX_FLUSH_INTERVAL,
DEFAULT_MAX_MEMORY_CONSUMPTION);
Expand Down
10 changes: 10 additions & 0 deletions sdk-java/src/main/java/com/spotify/confidence/FlagReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.spotify.confidence;

import java.io.Closeable;

public interface FlagReader extends Closeable {

<T> T getValue(String key, T defaultValue);

<T> FlagEvaluation<T> getEvaluation(String key, T defaultValue);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.spotify.confidence;

import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse;
import java.io.Closeable;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

public interface FlagReaderForProvider extends Contextual, Closeable {
CompletableFuture<ResolveFlagsResponse> resolveFlags(String flagName, String providerId);

FlagReaderForProvider withContext(ConfidenceValue.Struct context);

default FlagReaderForProvider withContext(Map<String, ConfidenceValue> context) {
return withContext(ConfidenceValue.Struct.of(context));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ interface FlagResolver {
void close();

public CompletableFuture<ResolveFlagsResponse> resolve(
String flag, Struct context, Boolean isProvider);
String flag, Struct context, String providerId);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.spotify.confidence;

import com.spotify.confidence.ConfidenceValue.Struct;
import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse;
import java.io.Closeable;
import java.util.concurrent.CompletableFuture;

interface FlagResolverClient extends Closeable {
CompletableFuture<ResolveFlagsResponse> resolveFlags(
String flag, ConfidenceValue.Struct context, Boolean isProvider);
String flag, Struct context, String providerId);

default CompletableFuture<ResolveFlagsResponse> resolveFlags(String flag, String providerId) {
return resolveFlags(flag, ConfidenceValue.Struct.builder().build(), providerId);
}
}
Loading

0 comments on commit 7c98857

Please sign in to comment.