Skip to content

Commit 321f8be

Browse files
authored
feat: Make the eventing provider specific instead of being singleton (#75)
Signed-off-by: vahid torkaman <[email protected]>
1 parent f4ba35d commit 321f8be

File tree

12 files changed

+222
-111
lines changed

12 files changed

+222
-111
lines changed

OpenFeature/src/main/java/dev/openfeature/sdk/FeatureProvider.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package dev.openfeature.sdk
22

3-
interface FeatureProvider {
3+
import dev.openfeature.sdk.events.EventObserver
4+
import dev.openfeature.sdk.events.ProviderStatus
5+
6+
interface FeatureProvider : EventObserver, ProviderStatus {
47
val hooks: List<Hook<*>>
58
val metadata: ProviderMetadata
69

OpenFeature/src/main/java/dev/openfeature/sdk/NoOpProvider.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package dev.openfeature.sdk
22

3+
import dev.openfeature.sdk.events.OpenFeatureEvents
4+
import kotlinx.coroutines.flow.Flow
5+
import kotlinx.coroutines.flow.flowOf
6+
37
class NoOpProvider(override val hooks: List<Hook<*>> = listOf()) : FeatureProvider {
48
override val metadata: ProviderMetadata = NoOpProviderMetadata("No-op provider")
59
override fun initialize(initialContext: EvaluationContext?) {
@@ -57,5 +61,9 @@ class NoOpProvider(override val hooks: List<Hook<*>> = listOf()) : FeatureProvid
5761
return ProviderEvaluation(defaultValue, "Passed in default", Reason.DEFAULT.toString())
5862
}
5963

64+
override fun observe(): Flow<OpenFeatureEvents> = flowOf()
65+
66+
override fun isProviderReady(): Boolean = true
67+
6068
data class NoOpProviderMetadata(override val name: String?) : ProviderMetadata
6169
}

OpenFeature/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.kt

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
package dev.openfeature.sdk
22

3-
import dev.openfeature.sdk.events.EventHandler
4-
import dev.openfeature.sdk.events.OpenFeatureEvents
5-
import dev.openfeature.sdk.events.observe
6-
import kotlinx.coroutines.CoroutineDispatcher
7-
83
@Suppress("TooManyFunctions")
94
object OpenFeatureAPI {
105
private var provider: FeatureProvider? = null
@@ -23,10 +18,6 @@ object OpenFeatureAPI {
2318
return provider
2419
}
2520

26-
inline fun <reified T : OpenFeatureEvents> observeEvents(dispatcher: CoroutineDispatcher) =
27-
EventHandler.eventsObserver(dispatcher)
28-
.observe<T>()
29-
3021
fun clearProvider() {
3122
provider = null
3223
}

OpenFeature/src/main/java/dev/openfeature/sdk/async/AsyncClient.kt

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package dev.openfeature.sdk.async
22

3-
import dev.openfeature.sdk.OpenFeatureClient
3+
import dev.openfeature.sdk.Client
4+
import dev.openfeature.sdk.FeatureProvider
45
import dev.openfeature.sdk.Value
5-
import kotlinx.coroutines.CoroutineDispatcher
66
import kotlinx.coroutines.flow.Flow
77
import kotlinx.coroutines.flow.distinctUntilChanged
88
import kotlinx.coroutines.flow.map
@@ -16,10 +16,11 @@ interface AsyncClient {
1616
}
1717

1818
internal class AsyncClientImpl(
19-
private val client: OpenFeatureClient,
20-
private val dispatcher: CoroutineDispatcher
19+
private val client: Client,
20+
private val provider: FeatureProvider
2121
) : AsyncClient {
22-
private fun <T> observeEvents(callback: () -> T) = observeProviderReady(dispatcher)
22+
private fun <T> observeEvents(callback: () -> T) = provider
23+
.observeProviderReady()
2324
.map { callback() }
2425
.distinctUntilChanged()
2526

OpenFeature/src/main/java/dev/openfeature/sdk/async/Extensions.kt

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,50 @@
11
package dev.openfeature.sdk.async
22

3+
import dev.openfeature.sdk.FeatureProvider
4+
import dev.openfeature.sdk.OpenFeatureAPI
35
import dev.openfeature.sdk.OpenFeatureClient
4-
import dev.openfeature.sdk.events.EventHandler
56
import dev.openfeature.sdk.events.OpenFeatureEvents
67
import dev.openfeature.sdk.events.observe
78
import kotlinx.coroutines.CoroutineDispatcher
89
import kotlinx.coroutines.CoroutineScope
910
import kotlinx.coroutines.Dispatchers
1011
import kotlinx.coroutines.cancel
12+
import kotlinx.coroutines.flow.Flow
1113
import kotlinx.coroutines.flow.onStart
1214
import kotlinx.coroutines.flow.take
1315
import kotlinx.coroutines.launch
1416
import kotlinx.coroutines.suspendCancellableCoroutine
1517

16-
fun OpenFeatureClient.toAsync(dispatcher: CoroutineDispatcher = Dispatchers.IO): AsyncClient {
17-
return AsyncClientImpl(this, dispatcher)
18+
fun OpenFeatureClient.toAsync(): AsyncClient? {
19+
val provider = OpenFeatureAPI.getProvider()
20+
return provider?.let {
21+
AsyncClientImpl(
22+
this,
23+
it
24+
)
25+
}
1826
}
1927

20-
internal fun observeProviderReady(
21-
dispatcher: CoroutineDispatcher = Dispatchers.IO
22-
) = EventHandler.eventsObserver(dispatcher)
23-
.observe<OpenFeatureEvents.ProviderReady>()
28+
suspend fun OpenFeatureAPI.setProviderAndWait(
29+
provider: FeatureProvider,
30+
dispatcher: CoroutineDispatcher
31+
) {
32+
setProvider(provider)
33+
provider.awaitReady(dispatcher)
34+
}
35+
36+
internal fun FeatureProvider.observeProviderReady() = observe<OpenFeatureEvents.ProviderReady>()
2437
.onStart {
25-
if (EventHandler.providerStatus().isProviderReady()) {
38+
if (isProviderReady()) {
2639
this.emit(OpenFeatureEvents.ProviderReady)
2740
}
2841
}
2942

30-
suspend fun awaitProviderReady(
43+
inline fun <reified T : OpenFeatureEvents> OpenFeatureAPI.observeEvents(): Flow<T>? {
44+
return getProvider()?.observe<T>()
45+
}
46+
47+
suspend fun FeatureProvider.awaitReady(
3148
dispatcher: CoroutineDispatcher = Dispatchers.IO
3249
) = suspendCancellableCoroutine { continuation ->
3350
val coroutineScope = CoroutineScope(dispatcher)
@@ -40,8 +57,7 @@ suspend fun awaitProviderReady(
4057
}
4158

4259
coroutineScope.launch {
43-
EventHandler.eventsObserver()
44-
.observe<OpenFeatureEvents.ProviderError>()
60+
observe<OpenFeatureEvents.ProviderError>()
4561
.take(1)
4662
.collect {
4763
continuation.resumeWith(Result.failure(it.error))

OpenFeature/src/main/java/dev/openfeature/sdk/events/EventHandler.kt

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,28 @@ package dev.openfeature.sdk.events
22

33
import kotlinx.coroutines.CoroutineDispatcher
44
import kotlinx.coroutines.CoroutineScope
5-
import kotlinx.coroutines.Dispatchers
65
import kotlinx.coroutines.Job
76
import kotlinx.coroutines.cancelChildren
87
import kotlinx.coroutines.flow.Flow
98
import kotlinx.coroutines.flow.MutableSharedFlow
109
import kotlinx.coroutines.flow.MutableStateFlow
1110
import kotlinx.coroutines.flow.filterIsInstance
1211
import kotlinx.coroutines.launch
13-
import kotlin.reflect.KClass
1412

15-
interface ProviderStatus {
16-
fun isProviderReady(): Boolean
13+
interface EventObserver {
14+
fun observe(): Flow<OpenFeatureEvents>
1715
}
1816

19-
interface EventObserver {
20-
fun <T : OpenFeatureEvents> observe(kClass: KClass<T>): Flow<T>
17+
interface ProviderStatus {
18+
fun isProviderReady(): Boolean
2119
}
2220

2321
interface EventsPublisher {
2422
fun publish(event: OpenFeatureEvents)
2523
}
2624

27-
inline fun <reified T : OpenFeatureEvents> EventObserver.observe() = observe(T::class)
25+
inline fun <reified T : OpenFeatureEvents> EventObserver.observe() = observe()
26+
.filterIsInstance<T>()
2827

2928
class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPublisher, ProviderStatus {
3029
private val sharedFlow: MutableSharedFlow<OpenFeatureEvents> = MutableSharedFlow()
@@ -56,29 +55,9 @@ class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPubli
5655
}
5756
}
5857

59-
override fun <T : OpenFeatureEvents> observe(kClass: KClass<T>): Flow<T> = sharedFlow
60-
.filterIsInstance(kClass)
58+
override fun observe(): Flow<OpenFeatureEvents> = sharedFlow
6159

6260
override fun isProviderReady(): Boolean {
6361
return isProviderReady.value
6462
}
65-
66-
companion object {
67-
@Volatile
68-
private var instance: EventHandler? = null
69-
70-
private fun getInstance(dispatcher: CoroutineDispatcher) =
71-
instance ?: synchronized(this) {
72-
instance ?: create(dispatcher).also { instance = it }
73-
}
74-
75-
fun eventsObserver(dispatcher: CoroutineDispatcher = Dispatchers.IO): EventObserver =
76-
getInstance(dispatcher)
77-
internal fun providerStatus(dispatcher: CoroutineDispatcher = Dispatchers.IO): ProviderStatus =
78-
getInstance(dispatcher)
79-
fun eventsPublisher(dispatcher: CoroutineDispatcher = Dispatchers.IO): EventsPublisher =
80-
getInstance(dispatcher)
81-
82-
private fun create(dispatcher: CoroutineDispatcher) = EventHandler(dispatcher)
83-
}
8463
}

0 commit comments

Comments
 (0)