-
Notifications
You must be signed in to change notification settings - Fork 75
feat: State change notification for flags client #3025
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
Changes from 26 commits
af48d48
a0cc0cc
1d10ded
fe1b172
8035650
4d1923c
856631b
c9f9cbc
a44a582
2ffdd58
d6d6538
4478372
a913a5c
4a54161
949a693
be53823
d2b729d
6d79a8b
f341b57
1cb6beb
9332ebf
81a95bd
acda00f
5a288d8
7fdd225
740d7e7
952b5b8
c8b1143
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 |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ import com.datadog.android.core.InternalSdkCore | |
| import com.datadog.android.flags.internal.DatadogFlagsClient | ||
| import com.datadog.android.flags.internal.DefaultRumEvaluationLogger | ||
| import com.datadog.android.flags.internal.FlagsFeature | ||
| import com.datadog.android.flags.internal.FlagsStateManager | ||
| import com.datadog.android.flags.internal.LogWithPolicy | ||
| import com.datadog.android.flags.internal.NoOpFlagsClient | ||
| import com.datadog.android.flags.internal.NoOpRumEvaluationLogger | ||
|
|
@@ -28,6 +29,7 @@ import com.datadog.android.flags.internal.repository.NoOpFlagsRepository | |
| import com.datadog.android.flags.internal.repository.net.PrecomputeMapper | ||
| import com.datadog.android.flags.model.EvaluationContext | ||
| import com.datadog.android.flags.model.ResolutionDetails | ||
| import com.datadog.android.internal.utils.DDCoreSubscription | ||
| import org.json.JSONObject | ||
|
|
||
| /** | ||
|
|
@@ -148,6 +150,28 @@ interface FlagsClient { | |
| */ | ||
| fun <T : Any> resolve(flagKey: String, defaultValue: T): ResolutionDetails<T> | ||
|
|
||
| /** | ||
| * Observable interface for tracking client state changes. | ||
| * | ||
| * Provides three ways to observe state: | ||
| * - Synchronous: [StateObservable.getCurrentState] for immediate queries (Java-friendly) | ||
| * - Reactive: [StateObservable.flow] for coroutine-based updates (Kotlin) | ||
| * - Callback: [StateObservable.addListener] for traditional observers (Java-friendly) | ||
| * | ||
| * Example: | ||
| * ```kotlin | ||
| * // Synchronous | ||
| * val current = client.state.getCurrentState() | ||
| * | ||
| * // Reactive Flow | ||
| * client.state.flow.collect { state -> /* ... */ } | ||
|
||
| * | ||
| * // Callback | ||
| * client.state.addListener(listener) | ||
| * ``` | ||
| */ | ||
| val state: StateObservable | ||
typotter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Builder for creating [FlagsClient] instances with custom configuration. | ||
| * | ||
|
|
@@ -338,7 +362,8 @@ interface FlagsClient { | |
|
|
||
| // region Internal | ||
|
|
||
| internal const val FLAGS_CLIENT_EXECUTOR_NAME = "flags-client-executor" | ||
| internal const val FLAGS_NETWORK_EXECUTOR_NAME = "flags-network" | ||
| internal const val FLAGS_STATE_NOTIFICATION_EXECUTOR_NAME = "flags-state-notifications" | ||
|
|
||
| @Suppress("LongMethod") | ||
| internal fun createInternal( | ||
|
|
@@ -347,8 +372,11 @@ interface FlagsClient { | |
| flagsFeature: FlagsFeature, | ||
| name: String | ||
| ): FlagsClient { | ||
| val executorService = featureSdkCore.createSingleThreadExecutorService( | ||
| executorContext = FLAGS_CLIENT_EXECUTOR_NAME | ||
| val networkExecutorService = featureSdkCore.createSingleThreadExecutorService( | ||
| executorContext = FLAGS_NETWORK_EXECUTOR_NAME | ||
| ) | ||
| val stateNotificationExecutorService = featureSdkCore.createSingleThreadExecutorService( | ||
| executorContext = FLAGS_STATE_NOTIFICATION_EXECUTOR_NAME | ||
| ) | ||
|
|
||
| val datadogContext = (featureSdkCore as InternalSdkCore).getDatadogContext() | ||
|
|
@@ -400,12 +428,19 @@ interface FlagsClient { | |
|
|
||
| val precomputeMapper = PrecomputeMapper(featureSdkCore.internalLogger) | ||
|
|
||
| val flagStateManager = FlagsStateManager( | ||
| DDCoreSubscription.create(), | ||
| stateNotificationExecutorService, | ||
| featureSdkCore.internalLogger | ||
| ) | ||
|
|
||
| val evaluationsManager = EvaluationsManager( | ||
| executorService = executorService, | ||
| executorService = networkExecutorService, | ||
| internalLogger = featureSdkCore.internalLogger, | ||
| flagsRepository = flagsRepository, | ||
| assignmentsReader = assignmentsDownloader, | ||
| precomputeMapper = precomputeMapper | ||
| precomputeMapper = precomputeMapper, | ||
| flagStateManager = flagStateManager | ||
| ) | ||
|
|
||
| val rumEvaluationLogger = createRumEvaluationLogger(featureSdkCore) | ||
|
|
@@ -416,7 +451,8 @@ interface FlagsClient { | |
| flagsRepository = flagsRepository, | ||
| flagsConfiguration = configuration, | ||
| rumEvaluationLogger = rumEvaluationLogger, | ||
| processor = flagsFeature.processor | ||
| processor = flagsFeature.processor, | ||
| flagStateManager = flagStateManager | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /* | ||
| * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
| * This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
| * Copyright 2016-Present Datadog, Inc. | ||
| */ | ||
|
|
||
| package com.datadog.android.flags | ||
|
|
||
| import com.datadog.android.flags.model.FlagsClientState | ||
|
|
||
| /** | ||
| * Listener interface for receiving state change notifications from a [FlagsClient]. | ||
| * | ||
| * Implementations of this interface can be registered with a [FlagsClient] to receive | ||
| * callbacks whenever the client's state changes. | ||
| */ | ||
| interface FlagsStateListener { | ||
| /** | ||
| * Called when the state of the [FlagsClient] changes. | ||
| * | ||
| * @param newState The new state of the client. If the state is [FlagsClientState.Error], | ||
| * the error details are contained within the state object itself. | ||
| */ | ||
| fun onStateChanged(newState: FlagsClientState) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| /* | ||
| * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
| * This product includes software developed at Datadog (https://www.datadoghq.com/). | ||
| * Copyright 2016-Present Datadog, Inc. | ||
| */ | ||
|
|
||
| package com.datadog.android.flags | ||
|
|
||
| import com.datadog.android.flags.model.FlagsClientState | ||
|
|
||
| /** | ||
| * Observable interface for tracking [FlagsClient] state changes. | ||
| * | ||
| * This interface provides two ways to observe state: | ||
| * 1. **Synchronous getter**: [getCurrentState] for immediate state queries | ||
| * 2. **Callback pattern**: [addListener]/[removeListener] for reactive observers | ||
| * | ||
| * ## Usage Examples | ||
| * | ||
| * ```kotlin | ||
| * // Synchronous getter | ||
| * val current = client.state.getCurrentState() | ||
| * if (current is FlagsClientState.Ready) { | ||
| * // Proceed | ||
| * } | ||
| * | ||
| * // Callback pattern | ||
| * client.state.addListener(object : FlagsStateListener { | ||
| * override fun onStateChanged(newState: FlagsClientState) { | ||
| * // Handle state change | ||
| * } | ||
| * }) | ||
| * ``` | ||
| */ | ||
| interface StateObservable { | ||
| /** | ||
| * Returns the current state synchronously. | ||
| * | ||
| * This method is safe to call from any thread. | ||
| * | ||
| * @return The current [FlagsClientState]. | ||
| */ | ||
| fun getCurrentState(): FlagsClientState | ||
typotter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Registers a listener to receive state change notifications. | ||
| * | ||
| * The listener will immediately receive the current state upon registration, | ||
| * then be notified of all future state changes. | ||
| * | ||
| * @param listener The [FlagsStateListener] to register. | ||
| */ | ||
| fun addListener(listener: FlagsStateListener) | ||
typotter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Unregisters a previously registered state listener. | ||
| * | ||
| * @param listener The [FlagsStateListener] to unregister. | ||
| */ | ||
| fun removeListener(listener: FlagsStateListener) | ||
| } | ||
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.
since coroutines are now removed from this module, this is not relevant
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.
fixed