Skip to content

Commit e25d572

Browse files
committed
Update multiprovider and tests
Signed-off-by: penguindan <[email protected]>
1 parent 2e75cd2 commit e25d572

File tree

8 files changed

+178
-142
lines changed

8 files changed

+178
-142
lines changed

kotlin-sdk/api/android/kotlin-sdk.api

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -674,17 +674,6 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P
674674
public fun toString ()Ljava/lang/String;
675675
}
676676

677-
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
678-
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
679-
public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
680-
public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged;
681-
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged;
682-
public fun equals (Ljava/lang/Object;)Z
683-
public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
684-
public fun hashCode ()I
685-
public fun toString ()Ljava/lang/String;
686-
}
687-
688677
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
689678
public fun <init> ()V
690679
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)V
@@ -719,17 +708,6 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P
719708
public fun toString ()Ljava/lang/String;
720709
}
721710

722-
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
723-
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
724-
public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
725-
public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling;
726-
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling;
727-
public fun equals (Ljava/lang/Object;)Z
728-
public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
729-
public fun hashCode ()I
730-
public fun toString ()Ljava/lang/String;
731-
}
732-
733711
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
734712
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
735713
public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
@@ -756,9 +734,13 @@ public final class dev/openfeature/kotlin/sdk/exceptions/ErrorCode : java/lang/E
756734
}
757735

758736
public abstract class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError : java/lang/Exception {
737+
public static final field Companion Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$Companion;
759738
public abstract fun errorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;
760739
}
761740

741+
public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$Companion {
742+
}
743+
762744
public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$FlagNotFoundError : dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError {
763745
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
764746
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V

kotlin-sdk/api/jvm/kotlin-sdk.api

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -674,17 +674,6 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P
674674
public fun toString ()Ljava/lang/String;
675675
}
676676

677-
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
678-
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
679-
public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
680-
public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged;
681-
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged;
682-
public fun equals (Ljava/lang/Object;)Z
683-
public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
684-
public fun hashCode ()I
685-
public fun toString ()Ljava/lang/String;
686-
}
687-
688677
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
689678
public fun <init> ()V
690679
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)V
@@ -719,17 +708,6 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P
719708
public fun toString ()Ljava/lang/String;
720709
}
721710

722-
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
723-
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
724-
public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
725-
public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling;
726-
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling;
727-
public fun equals (Ljava/lang/Object;)Z
728-
public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
729-
public fun hashCode ()I
730-
public fun toString ()Ljava/lang/String;
731-
}
732-
733711
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
734712
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
735713
public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
@@ -756,9 +734,13 @@ public final class dev/openfeature/kotlin/sdk/exceptions/ErrorCode : java/lang/E
756734
}
757735

758736
public abstract class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError : java/lang/Exception {
737+
public static final field Companion Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$Companion;
759738
public abstract fun errorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;
760739
}
761740

741+
public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$Companion {
742+
}
743+
762744
public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$FlagNotFoundError : dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError {
763745
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
764746
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V

kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
11
package dev.openfeature.kotlin.sdk
22

33
import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents
4-
import dev.openfeature.kotlin.sdk.exceptions.ErrorCode
4+
import dev.openfeature.kotlin.sdk.events.toOpenFeatureStatusError
55
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError
6-
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.FlagNotFoundError
7-
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.GeneralError
8-
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.InvalidContextError
9-
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ParseError
10-
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderFatalError
11-
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderNotReadyError
12-
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.TargetingKeyMissingError
13-
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.TypeMismatchError
146
import kotlinx.coroutines.CancellationException
157
import kotlinx.coroutines.CoroutineDispatcher
168
import kotlinx.coroutines.CoroutineScope
@@ -288,47 +280,11 @@ object OpenFeatureAPI {
288280
}
289281

290282
is OpenFeatureProviderEvents.ProviderError -> {
291-
val eventDetails = providerEvent.eventDetails
292-
if (eventDetails.errorCode != null) { // priority if EventDetails error has been provided
293-
val openFeatureError = constructOpenFeatureError(
294-
eventDetails.message ?: "Provider did not supply an error message",
295-
errorCode = eventDetails.errorCode
296-
)
297-
val status = if (eventDetails.errorCode == ErrorCode.PROVIDER_FATAL) {
298-
OpenFeatureStatus.Fatal(openFeatureError)
299-
} else {
300-
OpenFeatureStatus.Error(openFeatureError)
301-
}
302-
_statusFlow.emit(status)
303-
} else if (providerEvent.error != null) { // Deprecated impl
304-
val status = if (providerEvent.error is ProviderFatalError) {
305-
OpenFeatureStatus.Fatal(providerEvent.error)
306-
} else {
307-
OpenFeatureStatus.Error(providerEvent.error)
308-
}
309-
_statusFlow.emit(status)
310-
} else {
311-
_statusFlow.emit(
312-
OpenFeatureStatus.Error(GeneralError("Unknown error"))
313-
)
314-
}
283+
_statusFlow.emit(providerEvent.toOpenFeatureStatusError())
315284
}
316285

317286
else -> { // All other states should not be emitted from here
318287
}
319288
}
320289
}
321-
322-
private fun constructOpenFeatureError(errorMessage: String, errorCode: ErrorCode): OpenFeatureError {
323-
return when (errorCode) {
324-
ErrorCode.PROVIDER_NOT_READY -> ProviderNotReadyError()
325-
ErrorCode.FLAG_NOT_FOUND -> FlagNotFoundError(flagKey = null, errorMessage)
326-
ErrorCode.PARSE_ERROR -> ParseError(errorMessage)
327-
ErrorCode.TYPE_MISMATCH -> TypeMismatchError(errorMessage)
328-
ErrorCode.TARGETING_KEY_MISSING -> TargetingKeyMissingError(errorMessage)
329-
ErrorCode.INVALID_CONTEXT -> InvalidContextError(errorMessage)
330-
ErrorCode.GENERAL -> GeneralError(errorMessage)
331-
ErrorCode.PROVIDER_FATAL -> ProviderFatalError(errorMessage)
332-
}
333-
}
334290
}

kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package dev.openfeature.kotlin.sdk.events
22

3+
import dev.openfeature.kotlin.sdk.OpenFeatureStatus
34
import dev.openfeature.kotlin.sdk.exceptions.ErrorCode
45
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError
6+
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderFatalError
57

68
sealed class OpenFeatureProviderEvents {
79
data class EventDetails(
@@ -39,22 +41,34 @@ sealed class OpenFeatureProviderEvents {
3941
override val eventDetails: EventDetails
4042
) : OpenFeatureProviderEvents()
4143

42-
/**
43-
* The context associated with the provider has changed, and the provider has not yet reconciled its associated state.
44-
*/
45-
data class ProviderReconciling(
46-
override val eventDetails: EventDetails
47-
) : OpenFeatureProviderEvents()
48-
49-
/**
50-
* The context associated with the provider has changed, and the provider has reconciled its associated state.
51-
*/
52-
data class ProviderContextChanged(
53-
override val eventDetails: EventDetails
54-
) : OpenFeatureProviderEvents()
55-
5644
@Deprecated("Use ProviderError instead", ReplaceWith("ProviderError"))
5745
data object ProviderNotReady : OpenFeatureProviderEvents() {
5846
override val eventDetails = EventDetails()
5947
}
48+
}
49+
50+
internal fun OpenFeatureProviderEvents.ProviderError.toOpenFeatureStatusError(): OpenFeatureStatus {
51+
return when {
52+
eventDetails.errorCode != null -> {
53+
val openFeatureError = OpenFeatureError.fromMessageAndErrorCode(
54+
errorMessage = eventDetails.message ?: "Provider did not supply an error message",
55+
errorCode = eventDetails.errorCode
56+
)
57+
if (eventDetails.errorCode == ErrorCode.PROVIDER_FATAL) {
58+
OpenFeatureStatus.Fatal(openFeatureError)
59+
} else {
60+
OpenFeatureStatus.Error(openFeatureError)
61+
}
62+
}
63+
64+
error != null -> { // Deprecated implementation
65+
if (error is ProviderFatalError) {
66+
OpenFeatureStatus.Fatal(error)
67+
} else {
68+
OpenFeatureStatus.Error(error)
69+
}
70+
}
71+
72+
else -> OpenFeatureStatus.Error(OpenFeatureError.GeneralError("Unspecified error"))
73+
}
6074
}

kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,19 @@ sealed class OpenFeatureError : Exception() {
5959
return ErrorCode.PROVIDER_FATAL
6060
}
6161
}
62+
63+
companion object {
64+
internal fun fromMessageAndErrorCode(errorMessage: String, errorCode: ErrorCode): OpenFeatureError {
65+
return when (errorCode) {
66+
ErrorCode.PROVIDER_NOT_READY -> ProviderNotReadyError()
67+
ErrorCode.FLAG_NOT_FOUND -> FlagNotFoundError(flagKey = null, errorMessage)
68+
ErrorCode.PARSE_ERROR -> ParseError(errorMessage)
69+
ErrorCode.TYPE_MISMATCH -> TypeMismatchError(errorMessage)
70+
ErrorCode.TARGETING_KEY_MISSING -> TargetingKeyMissingError(errorMessage)
71+
ErrorCode.INVALID_CONTEXT -> InvalidContextError(errorMessage)
72+
ErrorCode.GENERAL -> GeneralError(errorMessage)
73+
ErrorCode.PROVIDER_FATAL -> ProviderFatalError(errorMessage)
74+
}
75+
}
76+
}
6277
}

kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProvider.kt

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dev.openfeature.kotlin.sdk.ProviderEvaluation
88
import dev.openfeature.kotlin.sdk.ProviderMetadata
99
import dev.openfeature.kotlin.sdk.Value
1010
import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents
11+
import dev.openfeature.kotlin.sdk.events.toOpenFeatureStatusError
1112
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError
1213
import kotlinx.coroutines.async
1314
import kotlinx.coroutines.awaitAll
@@ -158,7 +159,6 @@ class MultiProvider(
158159
*/
159160
internal fun getProviderCount(): Int = childFeatureProviders.size
160161

161-
// TODO Add distinctUntilChanged operator once EventDetails have been added
162162
override fun observe(): Flow<OpenFeatureProviderEvents> = eventFlow.asSharedFlow()
163163

164164
/**
@@ -197,12 +197,7 @@ class MultiProvider(
197197
is OpenFeatureProviderEvents.ProviderReady -> OpenFeatureStatus.Ready
198198
is OpenFeatureProviderEvents.ProviderNotReady -> OpenFeatureStatus.NotReady
199199
is OpenFeatureProviderEvents.ProviderStale -> OpenFeatureStatus.Stale
200-
is OpenFeatureProviderEvents.ProviderError ->
201-
if (event.error is OpenFeatureError.ProviderFatalError) {
202-
OpenFeatureStatus.Fatal(event.error)
203-
} else {
204-
OpenFeatureStatus.Error(event.error)
205-
}
200+
is OpenFeatureProviderEvents.ProviderError -> event.toOpenFeatureStatusError()
206201
}
207202

208203
val previousStatus = _statusFlow.value
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package dev.openfeature.kotlin.sdk
2+
3+
import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents
4+
import dev.openfeature.kotlin.sdk.events.toOpenFeatureStatusError
5+
import dev.openfeature.kotlin.sdk.exceptions.ErrorCode
6+
import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError
7+
import kotlin.test.Test
8+
import kotlin.test.assertEquals
9+
import kotlin.test.assertIs
10+
11+
class EventDetailsTests {
12+
13+
@Test
14+
fun providerErrorEventDetailsMapToFatal() {
15+
val evt = OpenFeatureProviderEvents.ProviderError(
16+
OpenFeatureProviderEvents.EventDetails(
17+
message = "message",
18+
errorCode = ErrorCode.PROVIDER_FATAL
19+
)
20+
)
21+
22+
val status = evt.toOpenFeatureStatusError()
23+
val fatal = assertIs<OpenFeatureStatus.Fatal>(status)
24+
val err = assertIs<OpenFeatureError.ProviderFatalError>(fatal.error)
25+
assertEquals("message", err.message)
26+
}
27+
28+
@Test
29+
fun providerErrorEventDetailsMapToError() {
30+
val evt = OpenFeatureProviderEvents.ProviderError(
31+
OpenFeatureProviderEvents.EventDetails(
32+
message = "flag missing",
33+
errorCode = ErrorCode.FLAG_NOT_FOUND
34+
)
35+
)
36+
37+
val status = evt.toOpenFeatureStatusError()
38+
val error = assertIs<OpenFeatureStatus.Error>(status)
39+
assertIs<OpenFeatureError.FlagNotFoundError>(error.error)
40+
assertEquals("flag missing", error.error.message)
41+
}
42+
43+
@Test
44+
fun providerErrorMapToFatal() {
45+
val evt = OpenFeatureProviderEvents.ProviderError(
46+
error = OpenFeatureError.ProviderFatalError("message")
47+
)
48+
49+
val status = evt.toOpenFeatureStatusError()
50+
val fatal = assertIs<OpenFeatureStatus.Fatal>(status)
51+
val err = assertIs<OpenFeatureError.ProviderFatalError>(fatal.error)
52+
assertEquals("message", err.message)
53+
}
54+
55+
@Test
56+
fun providerErrorMapToError() {
57+
val evt = OpenFeatureProviderEvents.ProviderError(
58+
error = OpenFeatureError.InvalidContextError("message")
59+
)
60+
61+
val status = evt.toOpenFeatureStatusError()
62+
val fatal = assertIs<OpenFeatureStatus.Error>(status)
63+
val err = assertIs<OpenFeatureError.InvalidContextError>(fatal.error)
64+
assertEquals("message", err.message)
65+
}
66+
67+
@Test
68+
fun providerErrorMapToUnspecifiedError() {
69+
val evt = OpenFeatureProviderEvents.ProviderError()
70+
71+
val status = evt.toOpenFeatureStatusError()
72+
val fatal = assertIs<OpenFeatureStatus.Error>(status)
73+
assertIs<OpenFeatureError.GeneralError>(fatal.error)
74+
}
75+
}

0 commit comments

Comments
 (0)