From a5991cb1515e8ae7f41bcc199e2b8ec7f2febf33 Mon Sep 17 00:00:00 2001 From: vahid torkaman Date: Thu, 7 Dec 2023 11:56:55 +0100 Subject: [PATCH] make the eventing provider specific instead of being singletone Signed-off-by: vahid torkaman --- .../dev/openfeature/sdk/FeatureProvider.kt | 5 +- .../java/dev/openfeature/sdk/NoOpProvider.kt | 8 ++ .../dev/openfeature/sdk/OpenFeatureAPI.kt | 9 -- .../dev/openfeature/sdk/async/AsyncClient.kt | 11 +- .../dev/openfeature/sdk/async/Extensions.kt | 38 ++++-- .../openfeature/sdk/events/EventHandler.kt | 35 ++---- .../dev/openfeature/sdk/EventsHandlerTest.kt | 110 +++++++++++------- .../openfeature/sdk/TestFeatureProvider.kt | 79 +++++++++++++ .../sdk/helpers/AlwaysBrokenProvider.kt | 7 ++ .../sdk/helpers/DoSomethingProvider.kt | 8 ++ build.gradle.kts | 4 +- 11 files changed, 216 insertions(+), 98 deletions(-) create mode 100644 OpenFeature/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt diff --git a/OpenFeature/src/main/java/dev/openfeature/sdk/FeatureProvider.kt b/OpenFeature/src/main/java/dev/openfeature/sdk/FeatureProvider.kt index 81ed906..9de1a04 100644 --- a/OpenFeature/src/main/java/dev/openfeature/sdk/FeatureProvider.kt +++ b/OpenFeature/src/main/java/dev/openfeature/sdk/FeatureProvider.kt @@ -1,6 +1,9 @@ package dev.openfeature.sdk -interface FeatureProvider { +import dev.openfeature.sdk.events.EventObserver +import dev.openfeature.sdk.events.ProviderStatus + +interface FeatureProvider : EventObserver, ProviderStatus { val hooks: List> val metadata: ProviderMetadata diff --git a/OpenFeature/src/main/java/dev/openfeature/sdk/NoOpProvider.kt b/OpenFeature/src/main/java/dev/openfeature/sdk/NoOpProvider.kt index c1d617f..5f96584 100644 --- a/OpenFeature/src/main/java/dev/openfeature/sdk/NoOpProvider.kt +++ b/OpenFeature/src/main/java/dev/openfeature/sdk/NoOpProvider.kt @@ -1,5 +1,9 @@ package dev.openfeature.sdk +import dev.openfeature.sdk.events.OpenFeatureEvents +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + class NoOpProvider(override val hooks: List> = listOf()) : FeatureProvider { override val metadata: ProviderMetadata = NoOpProviderMetadata("No-op provider") override fun initialize(initialContext: EvaluationContext?) { @@ -57,5 +61,9 @@ class NoOpProvider(override val hooks: List> = listOf()) : FeatureProvid return ProviderEvaluation(defaultValue, "Passed in default", Reason.DEFAULT.toString()) } + override fun observe(): Flow = flowOf() + + override fun isProviderReady(): Boolean = true + data class NoOpProviderMetadata(override val name: String?) : ProviderMetadata } \ No newline at end of file diff --git a/OpenFeature/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.kt b/OpenFeature/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.kt index 649d83b..fc247df 100644 --- a/OpenFeature/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.kt +++ b/OpenFeature/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.kt @@ -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 @@ -23,10 +18,6 @@ object OpenFeatureAPI { return provider } - inline fun observeEvents(dispatcher: CoroutineDispatcher) = - EventHandler.eventsObserver(dispatcher) - .observe() - fun clearProvider() { provider = null } diff --git a/OpenFeature/src/main/java/dev/openfeature/sdk/async/AsyncClient.kt b/OpenFeature/src/main/java/dev/openfeature/sdk/async/AsyncClient.kt index d730d6e..4772168 100644 --- a/OpenFeature/src/main/java/dev/openfeature/sdk/async/AsyncClient.kt +++ b/OpenFeature/src/main/java/dev/openfeature/sdk/async/AsyncClient.kt @@ -1,8 +1,8 @@ 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 import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @@ -16,10 +16,11 @@ interface AsyncClient { } internal class AsyncClientImpl( - private val client: OpenFeatureClient, - private val dispatcher: CoroutineDispatcher + private val client: Client, + private val provider: FeatureProvider ) : AsyncClient { - private fun observeEvents(callback: () -> T) = observeProviderReady(dispatcher) + private fun observeEvents(callback: () -> T) = provider + .observeProviderReady() .map { callback() } .distinctUntilChanged() diff --git a/OpenFeature/src/main/java/dev/openfeature/sdk/async/Extensions.kt b/OpenFeature/src/main/java/dev/openfeature/sdk/async/Extensions.kt index 52645cc..b7792ed 100644 --- a/OpenFeature/src/main/java/dev/openfeature/sdk/async/Extensions.kt +++ b/OpenFeature/src/main/java/dev/openfeature/sdk/async/Extensions.kt @@ -1,33 +1,50 @@ package dev.openfeature.sdk.async +import dev.openfeature.sdk.FeatureProvider +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.observe import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.onStart 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(): AsyncClient? { + val provider = OpenFeatureAPI.getProvider() + return provider?.let { + AsyncClientImpl( + this, + it + ) + } } -internal fun observeProviderReady( - dispatcher: CoroutineDispatcher = Dispatchers.IO -) = EventHandler.eventsObserver(dispatcher) - .observe() +internal fun FeatureProvider.observeProviderReady() = observe() .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) +} + +fun OpenFeatureAPI.observeEvents(): Flow? { + return getProvider()?.observe() +} + +suspend fun FeatureProvider.awaitProviderReady( dispatcher: CoroutineDispatcher = Dispatchers.IO ) = suspendCancellableCoroutine { continuation -> val coroutineScope = CoroutineScope(dispatcher) @@ -40,8 +57,7 @@ suspend fun awaitProviderReady( } coroutineScope.launch { - EventHandler.eventsObserver() - .observe() + observe() .take(1) .collect { continuation.resumeWith(Result.failure(it.error)) diff --git a/OpenFeature/src/main/java/dev/openfeature/sdk/events/EventHandler.kt b/OpenFeature/src/main/java/dev/openfeature/sdk/events/EventHandler.kt index d5813a0..7f1bc5c 100644 --- a/OpenFeature/src/main/java/dev/openfeature/sdk/events/EventHandler.kt +++ b/OpenFeature/src/main/java/dev/openfeature/sdk/events/EventHandler.kt @@ -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 @@ -10,21 +9,21 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch -import kotlin.reflect.KClass -interface ProviderStatus { - fun isProviderReady(): Boolean +interface EventObserver { + fun observe(): Flow } -interface EventObserver { - fun observe(kClass: KClass): Flow +interface ProviderStatus { + fun isProviderReady(): Boolean } interface EventsPublisher { fun publish(event: OpenFeatureEvents) } -inline fun EventObserver.observe() = observe(T::class) +inline fun EventObserver.observe() = observe() + .filterIsInstance() class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPublisher, ProviderStatus { private val sharedFlow: MutableSharedFlow = MutableSharedFlow() @@ -56,29 +55,9 @@ class EventHandler(dispatcher: CoroutineDispatcher) : EventObserver, EventsPubli } } - override fun observe(kClass: KClass): Flow = sharedFlow - .filterIsInstance(kClass) + override fun observe(): Flow = sharedFlow 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) - } } \ No newline at end of file diff --git a/OpenFeature/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt b/OpenFeature/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt index e019bc1..91145cd 100644 --- a/OpenFeature/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt +++ b/OpenFeature/src/test/java/dev/openfeature/sdk/EventsHandlerTest.kt @@ -22,37 +22,37 @@ import kotlin.time.Duration.Companion.milliseconds @OptIn(ExperimentalCoroutinesApi::class) class EventsHandlerTest { - @Test fun observing_event_observer_works() = runTest { - val eventObserver = EventHandler.eventsObserver() - val eventPublisher = EventHandler.eventsPublisher() + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) var emitted = false - val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { - eventObserver.observe() + val job = backgroundScope.launch(dispatcher) { + provider.observe() .take(1) .collect { emitted = true } } - - eventPublisher.publish(OpenFeatureEvents.ProviderReady) + provider.emitReady() job.join() Assert.assertTrue(emitted) } @Test fun multiple_subscribers_works() = runTest { - val eventObserver = EventHandler.eventsObserver() - val eventPublisher = EventHandler.eventsPublisher() + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) val numberOfSubscribers = 10 val parentJob = Job() var emitted = 0 repeat(numberOfSubscribers) { CoroutineScope(parentJob).launch(UnconfinedTestDispatcher(testScheduler)) { - eventObserver.observe() + provider.observe() .take(1) .collect { emitted += 1 @@ -60,28 +60,29 @@ class EventsHandlerTest { } } - eventPublisher.publish(OpenFeatureEvents.ProviderReady) + provider.emitReady() parentJob.children.forEach { it.join() } Assert.assertTrue(emitted == 10) } @Test fun canceling_one_subscriber_does_not_cancel_others() = runTest { - val eventObserver = EventHandler.eventsObserver() - val eventPublisher = EventHandler.eventsPublisher() + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) val numberOfSubscribers = 10 val parentJob = Job() var emitted = 0 val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { - eventObserver.observe() + provider.observe() .take(1) .collect {} } repeat(numberOfSubscribers) { CoroutineScope(parentJob).launch(UnconfinedTestDispatcher(testScheduler)) { - eventObserver.observe() + provider.observe() .take(1) .collect { emitted += 1 @@ -89,39 +90,43 @@ class EventsHandlerTest { } } job.cancel() - eventPublisher.publish(OpenFeatureEvents.ProviderReady) + provider.emitReady() parentJob.children.forEach { it.join() } Assert.assertTrue(emitted == 10) } @Test fun the_provider_status_stream_works() = runTest { - val eventPublisher = EventHandler.eventsPublisher() + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) var isProviderReady = false // observing the provider status after the provider ready event is published val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { - EventHandler.eventsObserver() - .observe() + provider.observe() .take(1) .collect { isProviderReady = true } } - eventPublisher.publish(OpenFeatureEvents.ProviderReady) + provider.emitReady() job.join() Assert.assertTrue(isProviderReady) } @Test + @kotlinx.coroutines.FlowPreview fun the_provider_status_stream_not_emitting_without_event_published() = runTest { var isProviderReady = false + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) // observing the provider status after the provider ready event is published val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { - EventHandler.eventsObserver() - .observe() + provider.observe() .timeout(10L.milliseconds) .collect { isProviderReady = true @@ -134,13 +139,15 @@ class EventsHandlerTest { @Test fun the_provider_status_stream_is_replays_current_status() = runTest { - val eventPublisher = EventHandler.eventsPublisher() - eventPublisher.publish(OpenFeatureEvents.ProviderReady) + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) + provider.emitReady() var isProviderReady = false // observing the provider status after the provider ready event is published val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { - observeProviderReady() + provider.observeProviderReady() .take(1) .collect { isProviderReady = true @@ -153,41 +160,50 @@ class EventsHandlerTest { @Test fun the_provider_becomes_stale() = runTest { - val eventPublisher = EventHandler.eventsPublisher() + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) var isProviderStale = false - val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) { - EventHandler.eventsObserver() - .observe() + val job = backgroundScope.launch(dispatcher) { + provider.observe() .take(1) .collect { isProviderStale = true } } - eventPublisher.publish(OpenFeatureEvents.ProviderReady) - eventPublisher.publish(OpenFeatureEvents.ProviderStale) + provider.emitReady() + provider.emitStale() job.join() - Assert.assertTrue(isProviderStale) } @Test fun observe_string_value_from_client_works() = runTest { - val testDispatcher = UnconfinedTestDispatcher(testScheduler) - val eventPublisher = EventHandler.eventsPublisher(testDispatcher) - eventPublisher.publish(OpenFeatureEvents.ProviderReady) + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) + + provider.emitReady() val key = "mykey" val default = "default" val resultTexts = mutableListOf() + OpenFeatureAPI.setProvider( + mock { + on { isProviderReady() } doReturn provider.isProviderReady() + on { observeProviderReady() } doReturn provider.observeProviderReady() + } + ) + val mockOpenFeatureClient = mock { on { getStringValue(key, default) } doReturn "text1" } // observing the provider status after the provider ready event is published - val job = backgroundScope.launch(testDispatcher) { - mockOpenFeatureClient.toAsync() + val job = backgroundScope.launch(dispatcher) { + mockOpenFeatureClient.toAsync()!! .observeStringValue(key, default) .take(2) .collect { @@ -198,15 +214,16 @@ class EventsHandlerTest { `when`(mockOpenFeatureClient.getStringValue(key, default)) .thenReturn("text2") - eventPublisher.publish(OpenFeatureEvents.ProviderReady) + provider.emitReady() job.join() Assert.assertEquals(listOf("text1", "text2"), resultTexts) } @Test fun observe_string_value_from_client_waits_until_provider_ready() = runTest { - val testDispatcher = UnconfinedTestDispatcher(testScheduler) - val eventPublisher = EventHandler.eventsPublisher(testDispatcher) + val dispatcher = UnconfinedTestDispatcher(testScheduler) + val eventHandler = EventHandler(dispatcher) + val provider = TestFeatureProvider(dispatcher, eventHandler) val key = "mykey" val default = "default" val resultTexts = mutableListOf() @@ -215,9 +232,16 @@ class EventsHandlerTest { on { getStringValue(key, default) } doReturn "text1" } + OpenFeatureAPI.setProvider( + mock { + on { isProviderReady() } doReturn provider.isProviderReady() + on { observeProviderReady() } doReturn provider.observeProviderReady() + } + ) + // observing the provider status after the provider ready event is published - val job = backgroundScope.launch(testDispatcher) { - mockOpenFeatureClient.toAsync() + val job = backgroundScope.launch(dispatcher) { + mockOpenFeatureClient.toAsync()!! .observeStringValue(key, default) .take(1) .collect { @@ -225,7 +249,7 @@ class EventsHandlerTest { } } - eventPublisher.publish(OpenFeatureEvents.ProviderReady) + provider.emitReady() job.join() Assert.assertEquals(listOf("text1"), resultTexts) } diff --git a/OpenFeature/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt b/OpenFeature/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt new file mode 100644 index 0000000..665cfb9 --- /dev/null +++ b/OpenFeature/src/test/java/dev/openfeature/sdk/TestFeatureProvider.kt @@ -0,0 +1,79 @@ +package dev.openfeature.sdk + +import dev.openfeature.sdk.events.EventHandler +import dev.openfeature.sdk.events.OpenFeatureEvents +import kotlinx.coroutines.CoroutineDispatcher + +class TestFeatureProvider( + dispatcher: CoroutineDispatcher, + private val eventHandler: EventHandler +) : FeatureProvider { + override val hooks: List> + get() = TODO("Not yet implemented") + override val metadata: ProviderMetadata + get() = TODO("Not yet implemented") + + override fun initialize(initialContext: EvaluationContext?) { + TODO("Not yet implemented") + } + + override fun shutdown() { + TODO("Not yet implemented") + } + + override fun onContextSet(oldContext: EvaluationContext?, newContext: EvaluationContext) { + TODO("Not yet implemented") + } + + override fun getBooleanEvaluation( + key: String, + defaultValue: Boolean, + context: EvaluationContext? + ): ProviderEvaluation { + TODO("Not yet implemented") + } + + override fun getStringEvaluation( + key: String, + defaultValue: String, + context: EvaluationContext? + ): ProviderEvaluation { + TODO("Not yet implemented") + } + + override fun getIntegerEvaluation( + key: String, + defaultValue: Int, + context: EvaluationContext? + ): ProviderEvaluation { + TODO("Not yet implemented") + } + + override fun getDoubleEvaluation( + key: String, + defaultValue: Double, + context: EvaluationContext? + ): ProviderEvaluation { + TODO("Not yet implemented") + } + + override fun getObjectEvaluation( + key: String, + defaultValue: Value, + context: EvaluationContext? + ): ProviderEvaluation { + TODO("Not yet implemented") + } + + override fun observe() = eventHandler.observe() + + override fun isProviderReady(): Boolean = eventHandler.isProviderReady() + + fun emitReady() { + eventHandler.publish(OpenFeatureEvents.ProviderReady) + } + + fun emitStale() { + eventHandler.publish(OpenFeatureEvents.ProviderStale) + } +} \ No newline at end of file diff --git a/OpenFeature/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt b/OpenFeature/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt index cfd6831..1660220 100644 --- a/OpenFeature/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt +++ b/OpenFeature/src/test/java/dev/openfeature/sdk/helpers/AlwaysBrokenProvider.kt @@ -6,7 +6,10 @@ import dev.openfeature.sdk.Hook import dev.openfeature.sdk.ProviderEvaluation import dev.openfeature.sdk.ProviderMetadata import dev.openfeature.sdk.Value +import dev.openfeature.sdk.events.OpenFeatureEvents 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()) : FeatureProvider { @@ -65,5 +68,9 @@ class AlwaysBrokenProvider(override var hooks: List> = listOf(), overrid throw FlagNotFoundError(key) } + override fun observe(): Flow = flow { } + + override fun isProviderReady(): Boolean = true + class AlwaysBrokenProviderMetadata(override val name: String? = "test") : ProviderMetadata } \ No newline at end of file diff --git a/OpenFeature/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt b/OpenFeature/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt index 7328dab..8373d4a 100644 --- a/OpenFeature/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt +++ b/OpenFeature/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt @@ -6,6 +6,9 @@ import dev.openfeature.sdk.Hook import dev.openfeature.sdk.ProviderEvaluation import dev.openfeature.sdk.ProviderMetadata import dev.openfeature.sdk.Value +import dev.openfeature.sdk.events.OpenFeatureEvents +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf class DoSomethingProvider( override val hooks: List> = listOf(), @@ -65,5 +68,10 @@ class DoSomethingProvider( ): ProviderEvaluation { return ProviderEvaluation(Value.Null) } + + override fun observe(): Flow = flowOf() + + override fun isProviderReady(): Boolean = true + class DoSomethingProviderMetadata(override val name: String? = "something") : ProviderMetadata } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index e40f414..311d6f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,7 +18,9 @@ nexusPublishing { this.repositories { sonatype { nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/")) - snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")) + snapshotRepositoryUrl.set( + uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + ) username = System.getenv("OSSRH_USERNAME") password = System.getenv("OSSRH_PASSWORD") }