Skip to content

Commit 1e5bd40

Browse files
committed
Use set context callback
1 parent 172905c commit 1e5bd40

File tree

2 files changed

+45
-59
lines changed

2 files changed

+45
-59
lines changed

features/dd-sdk-android-flags-openfeature/api/apiSurface

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,4 @@ class com.datadog.android.flags.openfeature.DatadogFlagsProvider : dev.openfeatu
1111
override fun shutdown()
1212
override fun observe(): kotlinx.coroutines.flow.Flow<dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents>
1313
companion object
14-
fun create(String = DEFAULT_CLIENT_NAME, com.datadog.android.api.SdkCore = Datadog.getInstance()): DatadogFlagsProvider
1514
fun com.datadog.android.flags.FlagsClient.asOpenFeatureProvider(): dev.openfeature.kotlin.sdk.FeatureProvider

features/dd-sdk-android-flags-openfeature/src/main/kotlin/com/datadog/android/flags/openfeature/DatadogFlagsProvider.kt

Lines changed: 45 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.datadog.android.Datadog
1010
import com.datadog.android.api.InternalLogger
1111
import com.datadog.android.api.SdkCore
1212
import com.datadog.android.api.feature.FeatureSdkCore
13+
import com.datadog.android.flags.EvaluationContextCallback
1314
import com.datadog.android.flags.FlagsClient
1415
import com.datadog.android.flags.FlagsStateListener
1516
import com.datadog.android.flags.model.FlagsClientState
@@ -93,45 +94,51 @@ class DatadogFlagsProvider private constructor(private val flagsClient: FlagsCli
9394
/**
9495
* Initializes the provider with the given evaluation context.
9596
*
96-
* Sets the initial evaluation context on the underlying [FlagsClient] and waits for
97-
* the provider to reach a ready state. Per the OpenFeature spec, this method blocks
98-
* until the provider is "ready" - where "ready" means a configuration has been set
99-
* and flags have been loaded.
97+
* Per the OpenFeature spec, this method blocks until the provider is "ready" - where
98+
* "ready" means the underlying [FlagsClient] has reached a Ready or Error state.
99+
*
100+
* If an initial context is provided, it will be set on the [FlagsClient] before waiting.
101+
* If no context is provided, the method still waits for the [FlagsClient] to reach
102+
* a ready state (e.g., from a previous setEvaluationContext call or cached data).
100103
*
101104
* The method suspends until the FlagsClient reaches either:
102-
* - [FlagsClientState.Ready]: Flags successfully loaded, resumes normally
103-
* - [FlagsClientState.Error]: Initialization failed, throws exception
105+
* - Ready: Flags successfully loaded, resumes normally
106+
* - Error: Initialization failed, throws exception
104107
*
105-
* @param initialContext The initial evaluation context to set
106-
* @throws Exception if initialization fails (FlagsClientState.Error)
108+
* @param initialContext The initial evaluation context to set (optional)
109+
* @throws Exception if initialization fails
107110
*/
108111
override suspend fun initialize(initialContext: OpenFeatureEvaluationContext?) {
109-
if (initialContext == null) return
112+
suspendCoroutine<Unit> { continuation ->
113+
val callback = object : EvaluationContextCallback {
114+
override fun onSuccess() {
115+
continuation.resume(Unit)
116+
}
110117

111-
// Trigger context setting first
112-
flagsClient.setEvaluationContext(initialContext.toDatadogEvaluationContext())
118+
override fun onFailure(error: Throwable) {
119+
continuation.resumeWithException(error)
120+
}
121+
}
113122

114-
// Then wait for Ready or Error state
115-
suspendCoroutine<Unit> { continuation ->
116-
val listener = object : FlagsStateListener {
117-
override fun onStateChanged(newState: FlagsClientState) {
118-
when (newState) {
119-
is FlagsClientState.Ready -> {
120-
flagsClient.state.removeListener(this)
121-
continuation.resume(Unit)
122-
}
123-
is FlagsClientState.Error -> {
124-
flagsClient.state.removeListener(this)
125-
continuation.resumeWithException(
126-
newState.error ?: Exception("Initialization failed")
127-
)
128-
}
129-
else -> {} // Still waiting (NotReady, Reconciling, Stale)
123+
val datadogContext = initialContext?.toDatadogEvaluationContext()
124+
if (datadogContext != null) {
125+
flagsClient.setEvaluationContext(datadogContext, callback)
126+
} else {
127+
// No context provided - check if already ready
128+
when (flagsClient.state.getCurrentState()) {
129+
is FlagsClientState.Ready -> continuation.resume(Unit)
130+
is FlagsClientState.Error -> continuation.resumeWithException(
131+
Exception("Provider not ready")
132+
)
133+
else -> {
134+
// Not ready yet - need to wait, but no context to set
135+
// This shouldn't happen in normal flow
136+
continuation.resumeWithException(
137+
Exception("Provider initialization requires a context")
138+
)
130139
}
131140
}
132141
}
133-
134-
flagsClient.state.addListener(listener)
135142
}
136143
}
137144

@@ -142,11 +149,7 @@ class DatadogFlagsProvider private constructor(private val flagsClient: FlagsCli
142149
* the provider is ready again or encounters an error. This allows the OpenFeature SDK
143150
* to emit PROVIDER_RECONCILING events while this method executes.
144151
*
145-
* The method suspends while the FlagsClient fetches updated flags for the new context,
146-
* and resumes when reaching a terminal state:
147-
* - [FlagsClientState.Ready]: Flags updated successfully, resumes normally
148-
* - [FlagsClientState.Stale]: Network failed but cached flags available, resumes normally
149-
* - [FlagsClientState.Error]: Unrecoverable error, throws exception
152+
* Uses the callback API to wait for completion without manual listener management.
150153
*
151154
* @param oldContext The previous evaluation context (unused)
152155
* @param newContext The new evaluation context to set
@@ -156,30 +159,18 @@ class DatadogFlagsProvider private constructor(private val flagsClient: FlagsCli
156159
oldContext: OpenFeatureEvaluationContext?,
157160
newContext: OpenFeatureEvaluationContext
158161
) {
159-
// Trigger context change first
160-
setContext(newContext)
161-
162-
// Then wait for Ready, Stale, or Error state
163162
suspendCoroutine<Unit> { continuation ->
164-
val listener = object : FlagsStateListener {
165-
override fun onStateChanged(newState: FlagsClientState) {
166-
when (newState) {
167-
FlagsClientState.Ready, FlagsClientState.Stale -> {
168-
flagsClient.state.removeListener(this)
169-
continuation.resume(Unit)
170-
}
171-
is FlagsClientState.Error -> {
172-
flagsClient.state.removeListener(this)
173-
continuation.resumeWithException(
174-
newState.error ?: Exception("Context reconciliation failed")
175-
)
176-
}
177-
else -> {} // Still reconciling (Reconciling, NotReady)
178-
}
163+
val callback = object : EvaluationContextCallback {
164+
override fun onSuccess() {
165+
continuation.resume(Unit)
166+
}
167+
168+
override fun onFailure(error: Throwable) {
169+
continuation.resumeWithException(error)
179170
}
180171
}
181172

182-
flagsClient.state.addListener(listener)
173+
flagsClient.setEvaluationContext(newContext.toDatadogEvaluationContext(), callback)
183174
}
184175
}
185176

@@ -349,10 +340,6 @@ class DatadogFlagsProvider private constructor(private val flagsClient: FlagsCli
349340
}
350341
}
351342

352-
private fun setContext(context: OpenFeatureEvaluationContext) {
353-
flagsClient.setEvaluationContext(context.toDatadogEvaluationContext())
354-
}
355-
356343
companion object {
357344
private const val PROVIDER_NAME = "Datadog Feature Flags Provider"
358345
private const val ERROR_REASON = "ERROR"

0 commit comments

Comments
 (0)