diff --git a/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt b/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt index 5f965840..fcbba651 100644 --- a/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt +++ b/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt @@ -63,7 +63,7 @@ class NoOpProvider(override val hooks: List> = listOf()) : FeatureProvid override fun observe(): Flow = flowOf() - override fun isProviderReady(): Boolean = true + override fun getProviderStatus(): OpenFeatureEvents = OpenFeatureEvents.ProviderReady data class NoOpProviderMetadata(override val name: String?) : ProviderMetadata } \ No newline at end of file diff --git a/android/src/main/java/dev/openfeature/sdk/async/Extensions.kt b/android/src/main/java/dev/openfeature/sdk/async/Extensions.kt index d27b78d2..af049766 100644 --- a/android/src/main/java/dev/openfeature/sdk/async/Extensions.kt +++ b/android/src/main/java/dev/openfeature/sdk/async/Extensions.kt @@ -5,6 +5,7 @@ import dev.openfeature.sdk.FeatureProvider import dev.openfeature.sdk.OpenFeatureAPI import dev.openfeature.sdk.OpenFeatureClient import dev.openfeature.sdk.events.OpenFeatureEvents +import dev.openfeature.sdk.events.isProviderReady import dev.openfeature.sdk.events.observe import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope diff --git a/android/src/main/java/dev/openfeature/sdk/events/EventHandler.kt b/android/src/main/java/dev/openfeature/sdk/events/EventHandler.kt index 7f1bc5ca..24141460 100644 --- a/android/src/main/java/dev/openfeature/sdk/events/EventHandler.kt +++ b/android/src/main/java/dev/openfeature/sdk/events/EventHandler.kt @@ -1,5 +1,6 @@ package dev.openfeature.sdk.events +import dev.openfeature.sdk.FeatureProvider import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -15,9 +16,12 @@ interface EventObserver { } interface ProviderStatus { - fun isProviderReady(): Boolean + fun getProviderStatus(): OpenFeatureEvents } +fun FeatureProvider.isProviderReady(): Boolean = + getProviderStatus() == OpenFeatureEvents.ProviderReady + interface EventsPublisher { fun publish(event: OpenFeatureEvents) } @@ -25,22 +29,25 @@ interface EventsPublisher { inline fun EventObserver.observe() = observe() .filterIsInstance() -class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPublisher, ProviderStatus { +class EventHandler(dispatcher: CoroutineDispatcher) : + EventObserver, + EventsPublisher, + ProviderStatus { private val sharedFlow: MutableSharedFlow = MutableSharedFlow() - private val isProviderReady = MutableStateFlow(false) + private val currentStatus: MutableStateFlow = + MutableStateFlow(OpenFeatureEvents.ProviderShutDown) private val job = Job() private val coroutineScope = CoroutineScope(job + dispatcher) init { coroutineScope.launch { sharedFlow.collect { + currentStatus.value = it when (it) { - is OpenFeatureEvents.ProviderReady -> isProviderReady.value = true - is OpenFeatureEvents.ProviderStale -> isProviderReady.value = false is OpenFeatureEvents.ProviderShutDown -> { - isProviderReady.value = false job.cancelChildren() } + else -> { // do nothing } @@ -57,7 +64,5 @@ class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPubli override fun observe(): Flow = sharedFlow - override fun isProviderReady(): Boolean { - return isProviderReady.value - } + override fun getProviderStatus(): OpenFeatureEvents = currentStatus.value } \ No newline at end of file diff --git a/android/src/main/java/dev/openfeature/sdk/events/OpenFeatureEvents.kt b/android/src/main/java/dev/openfeature/sdk/events/OpenFeatureEvents.kt index 90a1a8ce..5515ae36 100644 --- a/android/src/main/java/dev/openfeature/sdk/events/OpenFeatureEvents.kt +++ b/android/src/main/java/dev/openfeature/sdk/events/OpenFeatureEvents.kt @@ -1,8 +1,8 @@ package dev.openfeature.sdk.events -sealed class OpenFeatureEvents { - object ProviderReady : OpenFeatureEvents() - data class ProviderError(val error: Throwable) : OpenFeatureEvents() - object ProviderStale : OpenFeatureEvents() - object ProviderShutDown : OpenFeatureEvents() +sealed interface OpenFeatureEvents { + object ProviderReady : OpenFeatureEvents + data class ProviderError(val error: Throwable) : OpenFeatureEvents + object ProviderStale : OpenFeatureEvents + object ProviderShutDown : OpenFeatureEvents } \ No newline at end of file diff --git a/android/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt b/android/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt index 91145cdc..7116cd28 100644 --- a/android/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt +++ b/android/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt @@ -4,6 +4,7 @@ import dev.openfeature.sdk.async.observeProviderReady import dev.openfeature.sdk.async.toAsync import dev.openfeature.sdk.events.EventHandler import dev.openfeature.sdk.events.OpenFeatureEvents +import dev.openfeature.sdk.events.isProviderReady import dev.openfeature.sdk.events.observe import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -192,7 +193,7 @@ class EventsHandlerTest { OpenFeatureAPI.setProvider( mock { - on { isProviderReady() } doReturn provider.isProviderReady() + on { getProviderStatus() } doReturn provider.getProviderStatus() on { observeProviderReady() } doReturn provider.observeProviderReady() } ) @@ -234,7 +235,7 @@ class EventsHandlerTest { OpenFeatureAPI.setProvider( mock { - on { isProviderReady() } doReturn provider.isProviderReady() + on { getProviderStatus() } doReturn provider.getProviderStatus() on { observeProviderReady() } doReturn provider.observeProviderReady() } ) @@ -253,4 +254,33 @@ class EventsHandlerTest { job.join() Assert.assertEquals(listOf("text1"), resultTexts) } + + @Test + fun accessing_status_from_provider_works() = runTest { + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) + + Assert.assertEquals(OpenFeatureEvents.ProviderShutDown, provider.getProviderStatus()) + + provider.emitReady() + + Assert.assertEquals(OpenFeatureEvents.ProviderReady, provider.getProviderStatus()) + + provider.emitStale() + + Assert.assertEquals(OpenFeatureEvents.ProviderStale, provider.getProviderStatus()) + + val illegalStateException = IllegalStateException("test") + provider.emitError(illegalStateException) + + Assert.assertEquals( + OpenFeatureEvents.ProviderError(illegalStateException), + provider.getProviderStatus() + ) + + provider.shutdown() + + Assert.assertEquals(OpenFeatureEvents.ProviderShutDown, provider.getProviderStatus()) + } } \ No newline at end of file diff --git a/android/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt b/android/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt index 665cfb9c..fd6ea1fa 100644 --- a/android/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt +++ b/android/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt @@ -18,7 +18,7 @@ class TestFeatureProvider( } override fun shutdown() { - TODO("Not yet implemented") + eventHandler.publish(OpenFeatureEvents.ProviderShutDown) } override fun onContextSet(oldContext: EvaluationContext?, newContext: EvaluationContext) { @@ -67,7 +67,7 @@ class TestFeatureProvider( override fun observe() = eventHandler.observe() - override fun isProviderReady(): Boolean = eventHandler.isProviderReady() + override fun getProviderStatus(): OpenFeatureEvents = eventHandler.getProviderStatus() fun emitReady() { eventHandler.publish(OpenFeatureEvents.ProviderReady) @@ -76,4 +76,8 @@ class TestFeatureProvider( fun emitStale() { eventHandler.publish(OpenFeatureEvents.ProviderStale) } + + fun emitError(exception: Exception) { + eventHandler.publish(OpenFeatureEvents.ProviderError(exception)) + } } \ No newline at end of file diff --git a/android/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt b/android/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt index 16602203..27f484e4 100644 --- a/android/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt +++ b/android/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt @@ -11,7 +11,10 @@ import dev.openfeature.sdk.exceptions.OpenFeatureError.FlagNotFoundError import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow -class AlwaysBrokenProvider(override var hooks: List> = listOf(), override var metadata: ProviderMetadata = AlwaysBrokenProviderMetadata()) : +class AlwaysBrokenProvider( + override var hooks: List> = listOf(), + override var metadata: ProviderMetadata = AlwaysBrokenProviderMetadata() +) : FeatureProvider { override fun initialize(initialContext: EvaluationContext?) { // no-op @@ -70,7 +73,8 @@ class AlwaysBrokenProvider(override var hooks: List> = listOf(), overrid override fun observe(): Flow = flow { } - override fun isProviderReady(): Boolean = true + override fun getProviderStatus(): OpenFeatureEvents = + OpenFeatureEvents.ProviderError(FlagNotFoundError("test")) class AlwaysBrokenProviderMetadata(override val name: String? = "test") : ProviderMetadata } \ No newline at end of file diff --git a/android/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt b/android/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt index 8373d4a5..72da3bc5 100644 --- a/android/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt +++ b/android/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt @@ -71,7 +71,7 @@ class DoSomethingProvider( override fun observe(): Flow = flowOf() - override fun isProviderReady(): Boolean = true + override fun getProviderStatus(): OpenFeatureEvents = OpenFeatureEvents.ProviderReady class DoSomethingProviderMetadata(override val name: String? = "something") : ProviderMetadata } \ No newline at end of file