Skip to content

Commit

Permalink
make the eventing provider specific instead of being singletone
Browse files Browse the repository at this point in the history
  • Loading branch information
vahidlazio committed Dec 7, 2023
1 parent 4d19a0a commit e828a5a
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dev.openfeature.sdk

interface FeatureProvider {
import dev.openfeature.sdk.events.ProviderEventObserver

interface FeatureProvider : ProviderEventObserver {
val hooks: List<Hook<*>>
val metadata: ProviderMetadata

Expand Down
9 changes: 9 additions & 0 deletions OpenFeature/src/main/java/dev/openfeature/sdk/NoOpProvider.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package dev.openfeature.sdk

import dev.openfeature.sdk.events.OpenFeatureEvents
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlin.reflect.KClass

class NoOpProvider(override val hooks: List<Hook<*>> = listOf()) : FeatureProvider {
override val metadata: ProviderMetadata = NoOpProviderMetadata("No-op provider")
override fun initialize(initialContext: EvaluationContext?) {
Expand Down Expand Up @@ -57,5 +62,9 @@ class NoOpProvider(override val hooks: List<Hook<*>> = listOf()) : FeatureProvid
return ProviderEvaluation(defaultValue, "Passed in default", Reason.DEFAULT.toString())
}

override fun <T : OpenFeatureEvents> observe(kClass: KClass<T>): Flow<T> = flow { }

override fun isProviderReady(): Boolean = true

data class NoOpProviderMetadata(override val name: String?) : ProviderMetadata
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package dev.openfeature.sdk

import dev.openfeature.sdk.events.EventHandler
import dev.openfeature.sdk.events.OpenFeatureEvents
import dev.openfeature.sdk.events.observe
import kotlinx.coroutines.CoroutineDispatcher

@Suppress("TooManyFunctions")
object OpenFeatureAPI {
private var provider: FeatureProvider? = null
Expand All @@ -23,10 +18,6 @@ object OpenFeatureAPI {
return provider
}

inline fun <reified T : OpenFeatureEvents> observeEvents(dispatcher: CoroutineDispatcher) =
EventHandler.eventsObserver(dispatcher)
.observe<T>()

fun clearProvider() {
provider = null
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.openfeature.sdk.async

import dev.openfeature.sdk.OpenFeatureClient
import dev.openfeature.sdk.Client
import dev.openfeature.sdk.FeatureProvider
import dev.openfeature.sdk.Value
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
Expand All @@ -16,10 +17,12 @@ interface AsyncClient {
}

internal class AsyncClientImpl(
private val client: OpenFeatureClient,
private val client: Client,
private val provider: FeatureProvider,
private val dispatcher: CoroutineDispatcher
) : AsyncClient {
private fun <T> observeEvents(callback: () -> T) = observeProviderReady(dispatcher)
private fun <T> observeEvents(callback: () -> T) = provider
.observeProviderReady(dispatcher)
.map { callback() }
.distinctUntilChanged()

Expand Down
35 changes: 25 additions & 10 deletions OpenFeature/src/main/java/dev/openfeature/sdk/async/Extensions.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package dev.openfeature.sdk.async

import dev.openfeature.sdk.OpenFeatureAPI
import dev.openfeature.sdk.OpenFeatureClient
import dev.openfeature.sdk.events.EventHandler
import dev.openfeature.sdk.events.OpenFeatureEvents
import dev.openfeature.sdk.events.ProviderEventObserver
import dev.openfeature.sdk.events.observe
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
Expand All @@ -13,21 +14,36 @@ import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine

fun OpenFeatureClient.toAsync(dispatcher: CoroutineDispatcher = Dispatchers.IO): AsyncClient {
return AsyncClientImpl(this, dispatcher)
fun OpenFeatureClient.toAsync(
dispatcher: CoroutineDispatcher = Dispatchers.IO
): AsyncClient {
val provider = OpenFeatureAPI.getProvider()
requireNotNull(provider)
return AsyncClientImpl(
this,
provider,
dispatcher
)
}

internal fun observeProviderReady(
internal fun ProviderEventObserver.observeProviderReady(
dispatcher: CoroutineDispatcher = Dispatchers.IO
) = EventHandler.eventsObserver(dispatcher)
.observe<OpenFeatureEvents.ProviderReady>()
) = observe<OpenFeatureEvents.ProviderReady>()
.onStart {
if (EventHandler.providerStatus().isProviderReady()) {
if (isProviderReady()) {
this.emit(OpenFeatureEvents.ProviderReady)
}
}

suspend fun awaitProviderReady(
suspend fun OpenFeatureAPI.awaitProviderReady(
dispatcher: CoroutineDispatcher = Dispatchers.IO
) {
val provider = getProvider()
requireNotNull(provider)
return provider.awaitProviderReady(dispatcher)
}

suspend fun ProviderEventObserver.awaitProviderReady(
dispatcher: CoroutineDispatcher = Dispatchers.IO
) = suspendCancellableCoroutine { continuation ->
val coroutineScope = CoroutineScope(dispatcher)
Expand All @@ -40,8 +56,7 @@ suspend fun awaitProviderReady(
}

coroutineScope.launch {
EventHandler.eventsObserver()
.observe<OpenFeatureEvents.ProviderError>()
observe<OpenFeatureEvents.ProviderError>()
.take(1)
.collect {
continuation.resumeWith(Result.failure(it.error))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package dev.openfeature.sdk.events

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.flow.Flow
Expand All @@ -12,21 +11,18 @@ import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.launch
import kotlin.reflect.KClass

interface ProviderStatus {
fun isProviderReady(): Boolean
}

interface EventObserver {
interface ProviderEventObserver {
fun <T : OpenFeatureEvents> observe(kClass: KClass<T>): Flow<T>
fun isProviderReady(): Boolean
}

interface EventsPublisher {
fun publish(event: OpenFeatureEvents)
}

inline fun <reified T : OpenFeatureEvents> EventObserver.observe() = observe(T::class)
inline fun <reified T : OpenFeatureEvents> ProviderEventObserver.observe() = observe(T::class)

class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPublisher, ProviderStatus {
class EventHandler(dispatcher: CoroutineDispatcher) : ProviderEventObserver, EventsPublisher {
private val sharedFlow: MutableSharedFlow<OpenFeatureEvents> = MutableSharedFlow()
private val isProviderReady = MutableStateFlow(false)
private val job = Job()
Expand Down Expand Up @@ -62,23 +58,4 @@ class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPubli
override fun isProviderReady(): Boolean {
return isProviderReady.value
}

companion object {
@Volatile
private var instance: EventHandler? = null

private fun getInstance(dispatcher: CoroutineDispatcher) =
instance ?: synchronized(this) {
instance ?: create(dispatcher).also { instance = it }
}

fun eventsObserver(dispatcher: CoroutineDispatcher = Dispatchers.IO): EventObserver =
getInstance(dispatcher)
internal fun providerStatus(dispatcher: CoroutineDispatcher = Dispatchers.IO): ProviderStatus =
getInstance(dispatcher)
fun eventsPublisher(dispatcher: CoroutineDispatcher = Dispatchers.IO): EventsPublisher =
getInstance(dispatcher)

private fun create(dispatcher: CoroutineDispatcher) = EventHandler(dispatcher)
}
}
Loading

0 comments on commit e828a5a

Please sign in to comment.