-
Notifications
You must be signed in to change notification settings - Fork 5
Client consent screen #225
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
base: main
Are you sure you want to change the base?
Changes from all commits
b9dee91
d6063a1
163e27b
012c781
1eeca9e
24544f6
bf14807
0cfb60c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,9 +9,9 @@ | |
| import org.jspecify.annotations.Nullable; | ||
|
|
||
| import java.io.IOException; | ||
| import java.io.OutputStreamWriter; | ||
| import java.nio.file.Files; | ||
| import java.nio.file.Path; | ||
| import java.util.Objects; | ||
| import java.util.Properties; | ||
| import java.util.UUID; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
|
|
@@ -21,15 +21,7 @@ | |
| import static java.nio.charset.StandardCharsets.UTF_8; | ||
|
|
||
| @ApiStatus.Internal | ||
| public record SimpleConfig( | ||
| UUID serverId, | ||
| boolean enabled, | ||
| boolean additionalMetrics, | ||
| boolean debug, | ||
| boolean submitMetrics, | ||
| boolean errorTracking, | ||
| boolean firstRun | ||
| ) implements Config { | ||
| public final class SimpleConfig implements Config { | ||
| private static final int CONFIG_VERSION = 2; | ||
|
|
||
| private static final String COMMENT = """ | ||
|
|
@@ -58,7 +50,86 @@ | |
| Learn more at: https://faststats.dev/info | ||
|
|
||
| Since this is your first start with FastStats, submission will not start | ||
| until you restart the server to allow you to opt out if you prefer."""; | ||
| until after a restart, to allow you to opt out if you prefer."""; | ||
|
|
||
| private final Path file; | ||
| private final UUID serverId; | ||
| private final boolean debug; | ||
| private final boolean firstRun; | ||
| private volatile boolean additionalMetrics; | ||
| private volatile boolean enabled; | ||
| private volatile boolean errorTracking; | ||
| private volatile boolean submitMetrics; | ||
|
|
||
| public SimpleConfig( | ||
| final Path file, | ||
| final UUID serverId, | ||
| final boolean enabled, | ||
| final boolean additionalMetrics, | ||
| final boolean debug, | ||
| final boolean submitMetrics, | ||
| final boolean errorTracking, | ||
| final boolean firstRun | ||
| ) { | ||
| this.file = file; | ||
| this.serverId = serverId; | ||
| this.enabled = enabled; | ||
| this.additionalMetrics = additionalMetrics; | ||
| this.debug = debug; | ||
| this.submitMetrics = submitMetrics; | ||
| this.errorTracking = errorTracking; | ||
| this.firstRun = firstRun; | ||
| } | ||
|
|
||
| @Override | ||
| public UUID serverId() { | ||
| return serverId; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean enabled() { | ||
| return enabled; | ||
| } | ||
|
|
||
| public void enabled(final boolean enabled) { | ||
| this.enabled = enabled; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean additionalMetrics() { | ||
| return additionalMetrics; | ||
| } | ||
|
|
||
| public void additionalMetrics(final boolean additionalMetrics) { | ||
| this.additionalMetrics = additionalMetrics; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean debug() { | ||
| return debug; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean submitMetrics() { | ||
| return submitMetrics; | ||
| } | ||
|
|
||
| public void submitMetrics(final boolean submitMetrics) { | ||
| this.submitMetrics = submitMetrics; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean errorTracking() { | ||
| return errorTracking; | ||
| } | ||
|
|
||
| public void errorTracking(final boolean errorTracking) { | ||
| this.errorTracking = errorTracking; | ||
| } | ||
|
|
||
| public boolean firstRun() { | ||
| return firstRun; | ||
| } | ||
|
|
||
| @Contract(mutates = "io") | ||
| public static SimpleConfig read(final Path file, final LoggerFactory factory) throws RuntimeException { | ||
|
|
@@ -91,8 +162,7 @@ | |
| if (configVersion != null && configVersion < CONFIG_VERSION) | ||
| logger.info("Updating config version from %s to %s", configVersion, CONFIG_VERSION); | ||
| Files.createDirectories(file.getParent()); | ||
| try (final var out = Files.newOutputStream(file); | ||
| final var writer = new OutputStreamWriter(out, UTF_8)) { | ||
| try (final var writer = Files.newBufferedWriter(file, UTF_8)) { | ||
| final var store = new Properties(); | ||
|
|
||
| store.setProperty("enabled", Boolean.toString(enabled)); | ||
|
|
@@ -112,6 +182,7 @@ | |
| } | ||
|
|
||
| return new SimpleConfig( | ||
| file, | ||
| serverId, | ||
| enabled && enabledFlag, | ||
| enabled && enabledFlag && additionalMetrics, | ||
|
|
@@ -122,6 +193,23 @@ | |
| ); | ||
| } | ||
|
|
||
| @Contract(mutates = "io") | ||
| public void persist() throws RuntimeException { | ||
| final var properties = readOrEmpty(file); | ||
| if (properties == null) throw new IllegalStateException("Metrics config has not been initialized"); | ||
|
|
||
| properties.setProperty("enabled", Boolean.toString(enabled())); | ||
| properties.setProperty("submitAdditionalMetrics", Boolean.toString(additionalMetrics())); | ||
| properties.setProperty("submitErrors", Boolean.toString(errorTracking())); | ||
| properties.setProperty("submitMetrics", Boolean.toString(submitMetrics())); | ||
|
|
||
| try (final var writer = Files.newBufferedWriter(file, UTF_8)) { | ||
| properties.store(writer, COMMENT); | ||
| } catch (final IOException e) { | ||
| throw new RuntimeException("Failed to save metrics config", e); | ||
| } | ||
| } | ||
|
|
||
| // fixme: this code sucks ass | ||
| @Contract(value = "_, _, _, !null, _, _-> !null") | ||
| private static <T> @Nullable T parse( | ||
|
|
@@ -180,4 +268,33 @@ | |
| } | ||
| return true; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean equals(final Object object) { | ||
| if (this == object) return true; | ||
| if (!(object instanceof final SimpleConfig that)) return false; | ||
| return enabled == that.enabled | ||
| && additionalMetrics == that.additionalMetrics | ||
| && debug == that.debug | ||
| && submitMetrics == that.submitMetrics | ||
| && errorTracking == that.errorTracking | ||
| && firstRun == that.firstRun | ||
| && Objects.equals(serverId, that.serverId); | ||
| } | ||
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(serverId, enabled, additionalMetrics, debug, submitMetrics, errorTracking, firstRun); | ||
| } | ||
|
Comment on lines
+271
to
+288
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. equals and hashCode should not be overridden |
||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "SimpleConfig[serverId=" + serverId | ||
| + ", enabled=" + enabled | ||
| + ", additionalMetrics=" + additionalMetrics | ||
| + ", debug=" + debug | ||
| + ", submitMetrics=" + submitMetrics | ||
| + ", errorTracking=" + errorTracking | ||
| + ", firstRun=" + firstRun + ']'; | ||
| } | ||
| } | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. still dont like it |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package dev.faststats; | ||
|
|
||
| import java.util.Set; | ||
|
|
||
| /** | ||
| * A non-sensitive description of a live FastStats consumer. | ||
| * | ||
| * @since 0.29.2 | ||
| */ | ||
| public record FastStatsRegistration( | ||
| String projectName, | ||
| String sdkName, | ||
| String sdkVersion, | ||
| boolean metrics, | ||
| boolean errorTracking, | ||
| boolean featureFlags, | ||
| Set<String> additionalMetrics | ||
| ) { | ||
| public FastStatsRegistration { | ||
| additionalMetrics = Set.copyOf(additionalMetrics); | ||
| } | ||
| } |
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. still dont like it; also the javadocs are horrific |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| package dev.faststats; | ||
|
|
||
| import org.jetbrains.annotations.Contract; | ||
| import org.jspecify.annotations.Nullable; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.IdentityHashMap; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| /** | ||
| * Registry of contexts that opt into live onboarding lifecycle updates. | ||
| * | ||
| * @since 0.29.2 | ||
| */ | ||
| public final class FastStatsRegistry { | ||
| private static final FastStatsRegistry INSTANCE = new FastStatsRegistry(); | ||
|
|
||
| private final Map<SimpleContext, Entry> contexts = new IdentityHashMap<>(); | ||
| private @Nullable Config config; | ||
|
|
||
| private FastStatsRegistry() { | ||
| } | ||
|
|
||
| @Contract(pure = true) | ||
| public static FastStatsRegistry instance() { | ||
| return INSTANCE; | ||
| } | ||
|
|
||
| /** Registers a context and the shared mutable configuration used by its platform. */ | ||
| public synchronized void register(final SimpleContext context) { | ||
| if (this.config == null) this.config = context.getConfig(); | ||
| contexts.put(context, new Entry(context, context.registration())); | ||
| } | ||
|
|
||
| /** Removes a context when its platform lifecycle ends. */ | ||
| public synchronized void unregister(final SimpleContext context) { | ||
| contexts.remove(context); | ||
| if (contexts.isEmpty()) config = null; | ||
| } | ||
|
|
||
| /** Returns a snapshot of every registered FastStats consumer. */ | ||
| public synchronized List<FastStatsRegistration> registrations() { | ||
| return contexts.values().stream().map(Entry::registration).toList(); | ||
| } | ||
|
|
||
| /** Enables submission for all contexts using the shared configuration. */ | ||
| public void start() { | ||
| final var config = config(); | ||
| for (final var entry : entries()) { | ||
| entry.context().startSubmissions(config); | ||
| } | ||
| } | ||
|
|
||
| /** Disables submission for all contexts using the shared configuration. */ | ||
| public void shutdown() { | ||
| final var config = config(); | ||
| for (final var entry : entries()) { | ||
| entry.context().stopSubmissions(config); | ||
| } | ||
| } | ||
|
|
||
| private synchronized List<Entry> entries() { | ||
| return new ArrayList<>(contexts.values()); | ||
| } | ||
|
|
||
| public synchronized Config config() { | ||
| final var config = this.config; | ||
| if (config == null) throw new IllegalStateException("No FastStats contexts are registered"); | ||
| return config; | ||
| } | ||
|
|
||
| private record Entry( | ||
| SimpleContext context, | ||
| FastStatsRegistration registration | ||
| ) { | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create file