Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Total Confidence - functionality to send tracking events #88

Merged
merged 76 commits into from
Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
9cf202b
add the event sender engine and confidence value
vahidlazio Mar 12, 2024
44ffc31
Java format
fabriziodemaria Mar 12, 2024
24959bb
Moving files around
fabriziodemaria Mar 12, 2024
b18c28f
Smaller cleanups
fabriziodemaria Mar 12, 2024
5b3e228
Add getter to some Values
fabriziodemaria Mar 12, 2024
10e00f6
Add Closeable
fabriziodemaria Mar 12, 2024
b92440a
Refactoring signals and termination
fabriziodemaria Mar 12, 2024
60993e3
Making classes public
fabriziodemaria Mar 12, 2024
841e75e
Smaller refactoring
fabriziodemaria Mar 12, 2024
e55d5b7
grpc uploading
nicklasl Mar 12, 2024
179b622
Test ES engine uploads
fabriziodemaria Mar 12, 2024
3e45fc8
Formatter
fabriziodemaria Mar 12, 2024
c64ce18
send an actual event
nicklasl Mar 12, 2024
a74e11e
call shutdown() instead of shutdownNow()
nicklasl Mar 12, 2024
5317398
return false from uploader
nicklasl Mar 12, 2024
ea58bfb
make isStopped Atomic
nicklasl Mar 13, 2024
ce829de
add test to verify batches can be retried
nicklasl Mar 13, 2024
58026cf
throw exceptions from tests
nicklasl Mar 13, 2024
d85a77e
add shutdown messages and remove the sleep
vahidlazio Mar 13, 2024
c9be1eb
add tests for uploader
nicklasl Mar 13, 2024
7333bab
introduce clock and more tests
nicklasl Mar 13, 2024
40778d2
change and implement more value types
nicklasl Mar 13, 2024
58e4aaf
make close capped with a timeout
vahidlazio Mar 13, 2024
400e2d1
Make model classes public
fabriziodemaria Mar 13, 2024
e63caac
Add retries for non-transient errors
fabriziodemaria Mar 13, 2024
8856266
more tests on Value type
nicklasl Mar 13, 2024
21fe342
fix the close waiting for the events
vahidlazio Mar 13, 2024
35055b9
add test for engine close without events
vahidlazio Mar 13, 2024
4a573c6
remove the optional event
vahidlazio Mar 13, 2024
6f081e8
add final to get rid of warnings
nicklasl Mar 14, 2024
7fb3969
fix compilation error in uploader test
nicklasl Mar 14, 2024
3e5e038
Revert "Add retries for non-transient errors"
fabriziodemaria Mar 14, 2024
df13daf
Remove wrong import
fabriziodemaria Mar 14, 2024
2d172fb
fix lints
nicklasl Mar 14, 2024
ce07391
make the write queue not blocking
vahidlazio Mar 14, 2024
9b571a2
message fields directly on payload. context as a struct
nicklasl Mar 14, 2024
be9f8f4
fix formatting
nicklasl Mar 14, 2024
a5981ed
synchronized instead of semaphore
nicklasl Mar 14, 2024
50963bc
send error back from uploader if any errors occured
nicklasl Mar 14, 2024
66a9029
cleanup semaphores
vahidlazio Mar 14, 2024
2c62032
update tests
nicklasl Mar 15, 2024
d5ca81d
fix hanging thread.
nicklasl Mar 15, 2024
beeebfe
fix lint
nicklasl Mar 15, 2024
e3d8d46
fix: add uploader.close() and make the FakeUploader not clear on close()
nicklasl Mar 18, 2024
e180403
feat: initial eventsender and TC api
nicklasl Mar 18, 2024
8ab18e3
add more tests to contextual things
nicklasl Mar 18, 2024
1aef424
update some APIs
nicklasl Mar 18, 2024
060b302
feat: Add supported types to ConfidenceValue API (#91)
fabriziodemaria Mar 19, 2024
a254978
refactor: flag fetching is done via Confidence (#92)
nicklasl Mar 19, 2024
12640a5
reset flushpolicies after upload signal
nicklasl Mar 20, 2024
31c9048
feat: Limit ConfidenceValue.List to supported types (java types) (#96)
fabriziodemaria Mar 20, 2024
6bd8b3b
make Clock interface package private
nicklasl Mar 20, 2024
8b33545
Use async grpc and make sync in provider
nicklasl Mar 20, 2024
a9c6d61
style: Hide `eventDefinitions/` prefix from API (#99)
fabriziodemaria Mar 20, 2024
ce9188d
Fix formatting
fabriziodemaria Mar 20, 2024
dcbe813
chore: adjust contributors (#74)
nicklasl Mar 14, 2024
f29fe7b
docs: add readme about depending on snapshots (#84)
nicklasl Mar 14, 2024
d1f3a59
fix: support for mapping a OF Number to proto (#93)
nicklasl Mar 19, 2024
0490f64
chore(main): release 0.0.10 (#87)
github-actions[bot] Mar 20, 2024
d3a3f29
minor cleanups and refactors
nicklasl Mar 20, 2024
a497c44
add beta annotations
nicklasl Mar 20, 2024
debf2d3
Merge branch 'main' of github.com:spotify/confidence-openfeature-prov…
nicklasl Mar 20, 2024
42fc4ce
restore developer tag from pom.xml
nicklasl Mar 21, 2024
883bd1f
refactor getContext and remove removedKeys
nicklasl Mar 21, 2024
d301fa0
formatting
nicklasl Mar 21, 2024
41cbcc3
updating eventsender functions
nicklasl Mar 21, 2024
b4394ae
update FlagResolver class name
nicklasl Mar 21, 2024
975180b
update FlagResolver class name - part2
nicklasl Mar 21, 2024
5d17da6
use the clock to set event send time
nicklasl Mar 21, 2024
5409caf
proper completable futures
andreas-karlsson Mar 21, 2024
ff8a461
fixup! proper completable futures
andreas-karlsson Mar 21, 2024
02cd529
set OF context under key and spread in the FlagResolverClient
nicklasl Mar 22, 2024
0c60c59
refactor: Closable refactor (#104)
nicklasl Mar 22, 2024
14ce138
separte the gprc flag resolver from the flag resolver client, add tes…
vahidlazio Mar 25, 2024
4d53362
fixup! separte the gprc flag resolver from the flag resolver client, …
vahidlazio Mar 25, 2024
f6535b5
fixup! fixup! separte the gprc flag resolver from the flag resolver c…
vahidlazio Mar 25, 2024
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
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@
<name>Mattias Frånberg</name>
</developer>
<developer>
<id>andreas-karlsson</id>
<email>[email protected]</email>
<name>Andreas Karlsson</name>
<id>andreas-karlsson</id>
<email>[email protected]</email>
<name>Andreas Karlsson</name>
</developer>
</developers>

Expand Down
29 changes: 29 additions & 0 deletions src/main/java/com/spotify/confidence/BatchSizeFlushPolicy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.spotify.confidence;

import java.util.concurrent.atomic.AtomicInteger;

class BatchSizeFlushPolicy implements FlushPolicy {

private final int size;

public BatchSizeFlushPolicy(int size) {
this.size = size;
}

private final AtomicInteger currentCount = new AtomicInteger(0);

@Override
public void hit() {
currentCount.getAndIncrement();
}

@Override
public boolean shouldFlush() {
return currentCount.get() >= size;
}

@Override
public void reset() {
currentCount.set(0);
}
}
5 changes: 5 additions & 0 deletions src/main/java/com/spotify/confidence/Clock.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.spotify.confidence;

interface Clock {
long currentTimeSeconds();
}
226 changes: 226 additions & 0 deletions src/main/java/com/spotify/confidence/Confidence.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package com.spotify.confidence;

import com.google.common.annotations.Beta;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.io.Closer;
import com.spotify.confidence.shaded.flags.resolver.v1.ResolveFlagsResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collector;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;

@Beta
public abstract class Confidence implements EventSender, Closeable {

protected Map<String, ConfidenceValue> context = Maps.newHashMap();

private Confidence() {}

protected abstract ClientDelegate client();

protected Stream<Map.Entry<String, ConfidenceValue>> contextEntries() {
return context.entrySet().stream().filter(e -> !e.getValue().isNull());
}

@Override
public ConfidenceValue.Struct getContext() {

return contextEntries()
.collect(
Collector.of(
ImmutableMap.Builder<String, ConfidenceValue>::new,
ImmutableMap.Builder::put,
(b1, b2) -> b1.putAll(b2.build()),
builder -> ConfidenceValue.Struct.of(builder.build())));
}

@Override
public void setContext(ConfidenceValue.Struct context) {
this.context = Maps.newHashMap(context.asMap());
nicklasl marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public void updateContextEntry(String key, ConfidenceValue value) {
this.context.put(key, value);
nicklasl marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public void removeContextEntry(String key) {
this.context.put(key, ConfidenceValue.NULL_VALUE);
}

@Override
public void clearContext() {
this.context.clear();
nicklasl marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public Confidence withContext(ConfidenceValue.Struct context) {
final Confidence child = new ChildInstance(this);
child.setContext(context);
return child;
}

@Override
public Confidence withContext(Map<String, ConfidenceValue> context) {
return this.withContext(ConfidenceValue.of(context));
}

@Override
public void send(String name) {
try {
client().send(name, getContext(), Optional.empty());
} catch (IllegalStateException e) {
// swallow this exception
}
}

@Override
public void send(String name, ConfidenceValue.Struct message) {
try {
client().send(name, getContext(), Optional.of(message));
} catch (IllegalStateException e) {
// swallow this exception
}
}

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

static Confidence create(
EventSenderEngine eventSenderEngine, FlagResolverClient flagResolverClient) {
return new RootInstance(new ClientDelegate(flagResolverClient, eventSenderEngine));
}

public static Confidence.Builder builder(String clientSecret) {
return new Confidence.Builder(clientSecret);
}

private static class ClientDelegate implements FlagResolverClient, EventSenderEngine {
private final Closer closer = Closer.create();
private final FlagResolverClient flagResolverClient;
private final EventSenderEngine eventSenderEngine;

private ClientDelegate(
FlagResolverClient flagResolverClient, EventSenderEngine eventSenderEngine) {
this.flagResolverClient = closer.register(flagResolverClient);
this.eventSenderEngine = closer.register(eventSenderEngine);
}

@Override
public void send(
String name, ConfidenceValue.Struct context, Optional<ConfidenceValue.Struct> message) {
this.eventSenderEngine.send(name, context, message);
}

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

@Override
public void close() throws IOException {
closer.close();
}
}

private static class ChildInstance extends Confidence {

private final Confidence parent;
private boolean closed = false;

private ChildInstance(Confidence parent) {
this.parent = parent;
}

@Override
protected Stream<Map.Entry<String, ConfidenceValue>> contextEntries() {
final Set<String> ownKeys = context.keySet();
return Stream.concat(
parent.contextEntries().filter(entry -> !ownKeys.contains(entry.getKey())),
super.contextEntries());
}

@Override
protected ClientDelegate client() {
if (closed) throw new IllegalStateException("Resource closed");
return parent.client();
}

@Override
public void close() throws IOException {
closed = true;
}
}

private static class RootInstance extends Confidence {
@Nullable private ClientDelegate client;

private RootInstance(ClientDelegate client) {
this.client = client;
}

@Override
protected ClientDelegate client() {
if (client == null) throw new IllegalStateException("Resource closed");
return client;
}

@Override
public void close() throws IOException {
try {
client().close();
} finally {
client = null;
}
}
}

public static class Builder {
private final String clientSecret;

private final ManagedChannel DEFAULT_CHANNEL =
ManagedChannelBuilder.forAddress("edge-grpc.spotify.com", 443).build();
private ManagedChannel flagResolverManagedChannel = DEFAULT_CHANNEL;

public Builder(@Nonnull String clientSecret) {
this.clientSecret = clientSecret;
}

public Builder flagResolverManagedChannel(String host, int port) {
andreas-karlsson marked this conversation as resolved.
Show resolved Hide resolved
this.flagResolverManagedChannel =
ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
return this;
}

public Builder flagResolverManagedChannel(ManagedChannel managedChannel) {
this.flagResolverManagedChannel = managedChannel;
return this;
}

public Confidence build() {
final FlagResolverClient flagResolverClient =
new FlagResolverClientImpl(
new GrpcFlagResolver(clientSecret, flagResolverManagedChannel));
final SystemClock clock = new SystemClock();
final GrpcEventUploader uploader =
new GrpcEventUploader(clientSecret, clock, DEFAULT_CHANNEL);
final List<FlushPolicy> flushPolicies = ImmutableList.of(new BatchSizeFlushPolicy(5));
final EventSenderEngine engine = new EventSenderEngineImpl(flushPolicies, uploader, clock);
return Confidence.create(engine, flagResolverClient);
}
}
}
Loading
Loading