From 42c49ccf358f709ca4c70c0bbb4221385900dc8a Mon Sep 17 00:00:00 2001 From: Nicklas Lundin Date: Wed, 20 Dec 2023 16:12:35 +0100 Subject: [PATCH 1/2] feat: add getProviderStatus() function Signed-off-by: Nicklas Lundin --- .../java/dev/openfeature/sdk/NoOpProvider.kt | 2 ++ .../openfeature/sdk/events/EventHandler.kt | 12 +++++++- .../sdk/events/OpenFeatureEvents.kt | 10 +++---- .../dev/openfeature/sdk/EventsHandlerTest.kt | 29 +++++++++++++++++++ .../openfeature/sdk/TestFeatureProvider.kt | 8 ++++- .../sdk/helpers/AlwaysBrokenProvider.kt | 8 ++++- .../sdk/helpers/DoSomethingProvider.kt | 2 ++ 7 files changed, 63 insertions(+), 8 deletions(-) diff --git a/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt b/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt index 5f965840..0820ed54 100644 --- a/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt +++ b/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt @@ -65,5 +65,7 @@ class NoOpProvider(override val hooks: List> = listOf()) : FeatureProvid 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/events/EventHandler.kt b/android/src/main/java/dev/openfeature/sdk/events/EventHandler.kt index 7f1bc5ca..a63d225e 100644 --- a/android/src/main/java/dev/openfeature/sdk/events/EventHandler.kt +++ b/android/src/main/java/dev/openfeature/sdk/events/EventHandler.kt @@ -16,6 +16,8 @@ interface EventObserver { interface ProviderStatus { fun isProviderReady(): Boolean + + fun getProviderStatus(): OpenFeatureEvents } interface EventsPublisher { @@ -25,15 +27,21 @@ 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 @@ -60,4 +68,6 @@ class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPubli 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..ac61fe1a 100644 --- a/android/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt +++ b/android/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt @@ -253,4 +253,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..d9e21da0 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) { @@ -69,6 +69,8 @@ class TestFeatureProvider( override fun isProviderReady(): Boolean = eventHandler.isProviderReady() + override fun getProviderStatus(): OpenFeatureEvents = eventHandler.getProviderStatus() + fun emitReady() { eventHandler.publish(OpenFeatureEvents.ProviderReady) } @@ -76,4 +78,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..1c85c224 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 @@ -72,5 +75,8 @@ class AlwaysBrokenProvider(override var hooks: List> = listOf(), overrid 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..db7d3f80 100644 --- a/android/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt +++ b/android/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt @@ -73,5 +73,7 @@ class DoSomethingProvider( 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 From c79f68ff6c667bb336bef18020f72c37696d7b25 Mon Sep 17 00:00:00 2001 From: Nicklas Lundin Date: Wed, 20 Dec 2023 16:36:49 +0100 Subject: [PATCH 2/2] rewrite "isProviderReady()" to use "getProviderStatus()" Signed-off-by: Nicklas Lundin --- .../main/java/dev/openfeature/sdk/NoOpProvider.kt | 2 -- .../java/dev/openfeature/sdk/async/Extensions.kt | 1 + .../dev/openfeature/sdk/events/EventHandler.kt | 15 +++++---------- .../java/dev/openfeature/sdk/EventsHandlerTest.kt | 5 +++-- .../dev/openfeature/sdk/TestFeatureProvider.kt | 2 -- .../sdk/helpers/AlwaysBrokenProvider.kt | 2 -- .../sdk/helpers/DoSomethingProvider.kt | 2 -- 7 files changed, 9 insertions(+), 20 deletions(-) diff --git a/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt b/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt index 0820ed54..fcbba651 100644 --- a/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt +++ b/android/src/main/java/dev/openfeature/sdk/NoOpProvider.kt @@ -63,8 +63,6 @@ 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 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 a63d225e..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,11 +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) } @@ -32,7 +34,6 @@ class EventHandler(dispatcher: CoroutineDispatcher) : EventsPublisher, ProviderStatus { private val sharedFlow: MutableSharedFlow = MutableSharedFlow() - private val isProviderReady = MutableStateFlow(false) private val currentStatus: MutableStateFlow = MutableStateFlow(OpenFeatureEvents.ProviderShutDown) private val job = Job() @@ -43,12 +44,10 @@ class EventHandler(dispatcher: CoroutineDispatcher) : 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 } @@ -65,9 +64,5 @@ class EventHandler(dispatcher: CoroutineDispatcher) : 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/test/java/dev/openfeature/sdk/EventsHandlerTest.kt b/android/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt index ac61fe1a..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() } ) diff --git a/android/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt b/android/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt index d9e21da0..fd6ea1fa 100644 --- a/android/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt +++ b/android/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt @@ -67,8 +67,6 @@ class TestFeatureProvider( override fun observe() = eventHandler.observe() - override fun isProviderReady(): Boolean = eventHandler.isProviderReady() - override fun getProviderStatus(): OpenFeatureEvents = eventHandler.getProviderStatus() fun emitReady() { 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 1c85c224..27f484e4 100644 --- a/android/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt +++ b/android/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt @@ -73,8 +73,6 @@ class AlwaysBrokenProvider( override fun observe(): Flow = flow { } - override fun isProviderReady(): Boolean = true - override fun getProviderStatus(): OpenFeatureEvents = OpenFeatureEvents.ProviderError(FlagNotFoundError("test")) 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 db7d3f80..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,8 +71,6 @@ 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