From 635b654926a1619bd0e6507d90d0cfa1b136c75a Mon Sep 17 00:00:00 2001 From: penguindan Date: Mon, 25 Aug 2025 17:08:17 -0700 Subject: [PATCH 1/7] Update ProviderEvents Signed-off-by: penguindan --- kotlin-sdk/api/android/kotlin-sdk.api | 91 +++++++++++++++++-- kotlin-sdk/api/jvm/kotlin-sdk.api | 91 +++++++++++++++++-- .../openfeature/kotlin/sdk/OpenFeatureAPI.kt | 74 +++++++++++---- .../kotlin/sdk/OpenFeatureClient.kt | 10 +- .../sdk/events/OpenFeatureProviderEvents.kt | 59 ++++++++++-- .../kotlin/sdk/exceptions/OpenFeatureError.kt | 4 +- .../kotlin/sdk/DeveloperExperienceTests.kt | 9 +- .../kotlin/sdk/ProviderEventingTests.kt | 32 ++++--- .../kotlin/sdk/helpers/AutoHealingProvider.kt | 4 +- .../kotlin/sdk/helpers/DoSomethingProvider.kt | 14 +-- 10 files changed, 318 insertions(+), 70 deletions(-) diff --git a/kotlin-sdk/api/android/kotlin-sdk.api b/kotlin-sdk/api/android/kotlin-sdk.api index 54446ed1..d6aaaef1 100644 --- a/kotlin-sdk/api/android/kotlin-sdk.api +++ b/kotlin-sdk/api/android/kotlin-sdk.api @@ -311,6 +311,7 @@ public final class dev/openfeature/kotlin/sdk/OpenFeatureClient : dev/openfeatur public fun getObjectDetails (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;)Ldev/openfeature/kotlin/sdk/Value; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/Value; + public final fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringValue (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; @@ -632,34 +633,105 @@ public final class dev/openfeature/kotlin/sdk/Value$Structure$Companion { public final fun invoke (Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/Value$Structure; } -public abstract interface class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { +public abstract class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public abstract fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; +} + +public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails { + public fun ()V + public fun (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)V + public synthetic fun (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; + public final fun component4 ()Ljava/util/Map; + public final fun copy (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun equals (Ljava/lang/Object;)Z + public final fun getErrorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; + public final fun getEventMetadata ()Ljava/util/Map; + public final fun getFlagsChanged ()Ljava/util/List; + public final fun getMessage ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged; + 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; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public fun (Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)V - public final fun component1 ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError; - public final fun copy (Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError; - public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError; + public fun ()V + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)V + public synthetic fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun component2 ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError; public fun equals (Ljava/lang/Object;)Z public final fun getError ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError; + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling; + 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; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/exceptions/ErrorCode : java/lang/Enum { @@ -681,7 +753,8 @@ public abstract class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError : j } public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$FlagNotFoundError : dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError { - public fun (Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun errorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; public fun getMessage ()Ljava/lang/String; } diff --git a/kotlin-sdk/api/jvm/kotlin-sdk.api b/kotlin-sdk/api/jvm/kotlin-sdk.api index 54446ed1..d6aaaef1 100644 --- a/kotlin-sdk/api/jvm/kotlin-sdk.api +++ b/kotlin-sdk/api/jvm/kotlin-sdk.api @@ -311,6 +311,7 @@ public final class dev/openfeature/kotlin/sdk/OpenFeatureClient : dev/openfeatur public fun getObjectDetails (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;)Ldev/openfeature/kotlin/sdk/Value; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/Value; + public final fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringValue (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; @@ -632,34 +633,105 @@ public final class dev/openfeature/kotlin/sdk/Value$Structure$Companion { public final fun invoke (Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/Value$Structure; } -public abstract interface class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { +public abstract class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public abstract fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; +} + +public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails { + public fun ()V + public fun (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)V + public synthetic fun (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; + public final fun component4 ()Ljava/util/Map; + public final fun copy (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun equals (Ljava/lang/Object;)Z + public final fun getErrorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; + public final fun getEventMetadata ()Ljava/util/Map; + public final fun getFlagsChanged ()Ljava/util/List; + public final fun getMessage ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged; + 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; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public fun (Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)V - public final fun component1 ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError; - public final fun copy (Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError; - public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError; + public fun ()V + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)V + public synthetic fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun component2 ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError; public fun equals (Ljava/lang/Object;)Z public final fun getError ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError; + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling; + 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; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; + public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; + public fun equals (Ljava/lang/Object;)Z + public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/exceptions/ErrorCode : java/lang/Enum { @@ -681,7 +753,8 @@ public abstract class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError : j } public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$FlagNotFoundError : dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError { - public fun (Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun errorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; public fun getMessage ()Ljava/lang/String; } diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt index d7f48cc9..be5aadd5 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt @@ -1,7 +1,16 @@ package dev.openfeature.kotlin.sdk import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents +import dev.openfeature.kotlin.sdk.exceptions.ErrorCode import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.FlagNotFoundError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.GeneralError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.InvalidContextError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ParseError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderFatalError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderNotReadyError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.TargetingKeyMissingError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.TypeMismatchError import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -40,6 +49,9 @@ object OpenFeatureAPI { /** * A flow of [OpenFeatureStatus] that emits the current status of the SDK. + * + * Deprecated because calls to this should move to OpenFeatureClient according to + * https://openfeature.dev/specification/sections/flag-evaluation#17-provider-lifecycle-management */ val statusFlow: Flow get() = _statusFlow.distinctUntilChanged() @@ -261,32 +273,62 @@ object OpenFeatureAPI { inline fun observe(): Flow = providersFlow .flatMapLatest { it.observe() }.filterIsInstance() - private val handleProviderEvents: FlowCollector = FlowCollector { - when (it) { - OpenFeatureProviderEvents.ProviderReady -> { + /** + * Aligning the state management to + * https://openfeature.dev/specification/sections/events#requirement-535 + */ + private val handleProviderEvents: FlowCollector = FlowCollector { providerEvent -> + when (providerEvent) { + is OpenFeatureProviderEvents.ProviderReady -> { _statusFlow.emit(OpenFeatureStatus.Ready) } + is OpenFeatureProviderEvents.ProviderStale -> { + _statusFlow.emit(OpenFeatureStatus.Stale) + } + is OpenFeatureProviderEvents.ProviderError -> { - val status = if (it.error is OpenFeatureError.ProviderFatalError) { - OpenFeatureStatus.Fatal(it.error) + val eventDetails = providerEvent.eventDetails + if (eventDetails.errorCode != null) { // priority if EventDetails error has been provided + val openFeatureError = constructOpenFeatureError( + eventDetails.message ?: "Provider did not supply an error message", + errorCode = eventDetails.errorCode + ) + val status = if (eventDetails.errorCode == ErrorCode.PROVIDER_FATAL) { + OpenFeatureStatus.Fatal(openFeatureError) + } else { + OpenFeatureStatus.Error(openFeatureError) + } + _statusFlow.emit(status) + } else if (providerEvent.error != null) { // Deprecated impl + val status = if (providerEvent.error is ProviderFatalError) { + OpenFeatureStatus.Fatal(providerEvent.error) + } else { + OpenFeatureStatus.Error(providerEvent.error) + } + _statusFlow.emit(status) } else { - OpenFeatureStatus.Error(it.error) + _statusFlow.emit( + OpenFeatureStatus.Error(GeneralError("Unknown error")) + ) } - _statusFlow.emit(status) } - OpenFeatureProviderEvents.ProviderNotReady -> { - _statusFlow.emit(OpenFeatureStatus.NotReady) - } - - OpenFeatureProviderEvents.ProviderStale -> { - _statusFlow.emit(OpenFeatureStatus.Stale) + else -> { // All other states should not be emitted from here } + } + } - OpenFeatureProviderEvents.ProviderConfigurationChanged -> { - _statusFlow.emit(OpenFeatureStatus.Ready) - } + private fun constructOpenFeatureError(errorMessage: String, errorCode: ErrorCode): OpenFeatureError { + return when (errorCode) { + ErrorCode.PROVIDER_NOT_READY -> ProviderNotReadyError() + ErrorCode.FLAG_NOT_FOUND -> FlagNotFoundError(flagKey = null, errorMessage) + ErrorCode.PARSE_ERROR -> ParseError(errorMessage) + ErrorCode.TYPE_MISMATCH -> TypeMismatchError(errorMessage) + ErrorCode.TARGETING_KEY_MISSING -> TargetingKeyMissingError(errorMessage) + ErrorCode.INVALID_CONTEXT -> InvalidContextError(errorMessage) + ErrorCode.GENERAL -> GeneralError(errorMessage) + ErrorCode.PROVIDER_FATAL -> ProviderFatalError(errorMessage) } } } \ No newline at end of file diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt index 071f0ef3..102040cd 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt @@ -24,6 +24,13 @@ class OpenFeatureClient( this.hooks += hooks } + /** + * Calls from OpenFeatureAPI.statusFlow should move to this client according to + * https://openfeature.dev/specification/sections/flag-evaluation#17-provider-lifecycle-management + */ + @Suppress("DEPRECATION") + val providerStatusFlow = openFeatureAPI.statusFlow + override fun getBooleanValue(key: String, defaultValue: Boolean): Boolean { return getBooleanDetails(key, defaultValue).value } @@ -175,8 +182,7 @@ class OpenFeatureClient( val hints = options.hookHints var details = FlagEvaluationDetails(key, defaultValue) val provider = openFeatureAPI.getProvider() - val mergedHooks: List> = - provider.hooks + options.hooks + hooks + openFeatureAPI.hooks + val mergedHooks: List> = provider.hooks + options.hooks + hooks + openFeatureAPI.hooks val context = openFeatureAPI.getEvaluationContext() val hookCtx: HookContext = HookContext( key, diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt index a52ac9ad..2ba36540 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt @@ -1,13 +1,60 @@ package dev.openfeature.kotlin.sdk.events +import dev.openfeature.kotlin.sdk.exceptions.ErrorCode import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError -sealed interface OpenFeatureProviderEvents { - object ProviderReady : OpenFeatureProviderEvents +sealed class OpenFeatureProviderEvents { + data class EventDetails( + val flagsChanged: List = emptyList(), + val message: String? = null, + val errorCode: ErrorCode? = null, + val eventMetadata: Map = emptyMap() + ) + + abstract val eventDetails: EventDetails + + /** + * The provider is ready to perform flag evaluations. + */ + data class ProviderReady( + override val eventDetails: EventDetails + ) : OpenFeatureProviderEvents() + + /** + * The provider signaled an error. + */ + data class ProviderError( + override val eventDetails: EventDetails = EventDetails(), + @Deprecated("Please use eventDetails instead.") val error: OpenFeatureError? = null + ) : OpenFeatureProviderEvents() + + data class ProviderConfigurationChanged( + override val eventDetails: EventDetails + ) : OpenFeatureProviderEvents() + + /** + * The provider's cached state is no longer valid and may not be up-to-date with the source of truth. + */ + data class ProviderStale( + override val eventDetails: EventDetails + ) : OpenFeatureProviderEvents() + + /** + * The context associated with the provider has changed, and the provider has not yet reconciled its associated state. + */ + data class ProviderReconciling( + override val eventDetails: EventDetails + ) : OpenFeatureProviderEvents() + + /** + * The context associated with the provider has changed, and the provider has reconciled its associated state. + */ + data class ProviderContextChanged( + override val eventDetails: EventDetails + ) : OpenFeatureProviderEvents() @Deprecated("Use ProviderError instead", ReplaceWith("ProviderError")) - object ProviderNotReady : OpenFeatureProviderEvents - object ProviderStale : OpenFeatureProviderEvents - data class ProviderError(val error: OpenFeatureError) : OpenFeatureProviderEvents - object ProviderConfigurationChanged : OpenFeatureProviderEvents + data object ProviderNotReady : OpenFeatureProviderEvents() { + override val eventDetails = EventDetails() + } } \ No newline at end of file diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt index 2224ad37..c89991e4 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt @@ -9,8 +9,10 @@ sealed class OpenFeatureError : Exception() { } } - class FlagNotFoundError(flagKey: String) : OpenFeatureError() { + class FlagNotFoundError( + flagKey: String?, override val message: String = "Could not find flag named: $flagKey" + ) : OpenFeatureError() { override fun errorCode(): ErrorCode { return ErrorCode.FLAG_NOT_FOUND } diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/DeveloperExperienceTests.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/DeveloperExperienceTests.kt index 4e5036f9..09fe7636 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/DeveloperExperienceTests.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/DeveloperExperienceTests.kt @@ -211,6 +211,7 @@ class DeveloperExperienceTests { val emittedStatuses = mutableListOf() val job = launch { OpenFeatureAPI.statusFlow.collect { + println(it) emittedStatuses.add(it) } } @@ -350,10 +351,10 @@ class DeveloperExperienceTests { job.cancelAndJoin() assertEquals( listOf( - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderStale + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) ), staleEvents ) diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/ProviderEventingTests.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/ProviderEventingTests.kt index ff852901..bace3118 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/ProviderEventingTests.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/ProviderEventingTests.kt @@ -41,13 +41,17 @@ class ProviderEventingTests { ) { flow.emit( OpenFeatureProviderEvents.ProviderError( - OpenFeatureError.ProviderNotReadyError( + error = OpenFeatureError.ProviderNotReadyError( "test error" ) ) ) delay(healDelayMillis) - flow.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged) + flow.emit( + OpenFeatureProviderEvents.ProviderConfigurationChanged( + OpenFeatureProviderEvents.EventDetails() + ) + ) } override fun observe(): Flow = flow @@ -103,9 +107,9 @@ class ProviderEventingTests { testScheduler.advanceUntilIdle() assertEquals( listOf( - OpenFeatureProviderEvents.ProviderReady, - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderConfigurationChanged + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()) ), emittedEvents ) @@ -127,15 +131,15 @@ class ProviderEventingTests { job.cancelAndJoin() assertEquals( listOf( - OpenFeatureProviderEvents.ProviderReady, - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderReady, - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderStale, - OpenFeatureProviderEvents.ProviderConfigurationChanged + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()) ), emittedEvents ) diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/AutoHealingProvider.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/AutoHealingProvider.kt index ee9f7e4a..34409f13 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/AutoHealingProvider.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/AutoHealingProvider.kt @@ -25,13 +25,13 @@ class AutoHealingProvider( ready = false _events.emit( OpenFeatureProviderEvents.ProviderError( - OpenFeatureError.ProviderNotReadyError( + error = OpenFeatureError.ProviderNotReadyError( "AutoHealingProvider got an error. trying to heal" ) ) ) delay(healDelay) - _events.emit(OpenFeatureProviderEvents.ProviderReady) + _events.emit(OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails())) ready = true } diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/DoSomethingProvider.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/DoSomethingProvider.kt index 8a2b2a57..185b8eb4 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/DoSomethingProvider.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/DoSomethingProvider.kt @@ -27,7 +27,7 @@ open class DoSomethingProvider( override suspend fun initialize(initialContext: EvaluationContext?) { delay(1000) - events.emit(OpenFeatureProviderEvents.ProviderReady) + events.emit(OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails())) } override fun shutdown() { @@ -39,7 +39,7 @@ open class DoSomethingProvider( newContext: EvaluationContext ) { delay(500) - events.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged) + events.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails())) } override fun getBooleanEvaluation( @@ -101,8 +101,8 @@ class OverlyEmittingProvider(name: String) : DoSomethingProvider( oldContext: EvaluationContext?, newContext: EvaluationContext ) { - events.emit(OpenFeatureProviderEvents.ProviderStale) - events.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged) + events.emit(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails())) + events.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails())) } override fun track( @@ -111,8 +111,8 @@ class OverlyEmittingProvider(name: String) : DoSomethingProvider( details: TrackingEventDetails? ) { super.track(trackingEventName, context, details) - events.tryEmit(OpenFeatureProviderEvents.ProviderStale) - events.tryEmit(OpenFeatureProviderEvents.ProviderStale) - events.tryEmit(OpenFeatureProviderEvents.ProviderStale) + events.tryEmit(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails())) + events.tryEmit(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails())) + events.tryEmit(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails())) } } \ No newline at end of file From e25d5721fa20e9e5d854e52bb16e8275fa2bdaad Mon Sep 17 00:00:00 2001 From: penguindan Date: Fri, 5 Sep 2025 14:03:03 -0700 Subject: [PATCH 2/7] Update multiprovider and tests Signed-off-by: penguindan --- kotlin-sdk/api/android/kotlin-sdk.api | 26 +----- kotlin-sdk/api/jvm/kotlin-sdk.api | 26 +----- .../openfeature/kotlin/sdk/OpenFeatureAPI.kt | 48 +---------- .../sdk/events/OpenFeatureProviderEvents.kt | 42 ++++++---- .../kotlin/sdk/exceptions/OpenFeatureError.kt | 15 ++++ .../kotlin/sdk/multiprovider/MultiProvider.kt | 9 +-- .../kotlin/sdk/EventDetailsTests.kt | 75 ++++++++++++++++++ .../sdk/multiprovider/MultiProviderTests.kt | 79 +++++++++++-------- 8 files changed, 178 insertions(+), 142 deletions(-) create mode 100644 kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/EventDetailsTests.kt diff --git a/kotlin-sdk/api/android/kotlin-sdk.api b/kotlin-sdk/api/android/kotlin-sdk.api index bdadcbf8..57c0e178 100644 --- a/kotlin-sdk/api/android/kotlin-sdk.api +++ b/kotlin-sdk/api/android/kotlin-sdk.api @@ -674,17 +674,6 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P public fun toString ()Ljava/lang/String; } -public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V - public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged; - 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; - public fun equals (Ljava/lang/Object;)Z - public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { public fun ()V public fun (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 public fun toString ()Ljava/lang/String; } -public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V - public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling; - 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; - public fun equals (Ljava/lang/Object;)Z - public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V 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 } public abstract class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError : java/lang/Exception { + public static final field Companion Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$Companion; public abstract fun errorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; } +public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$Companion { +} + public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$FlagNotFoundError : dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError { public fun (Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V diff --git a/kotlin-sdk/api/jvm/kotlin-sdk.api b/kotlin-sdk/api/jvm/kotlin-sdk.api index bdadcbf8..57c0e178 100644 --- a/kotlin-sdk/api/jvm/kotlin-sdk.api +++ b/kotlin-sdk/api/jvm/kotlin-sdk.api @@ -674,17 +674,6 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P public fun toString ()Ljava/lang/String; } -public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V - public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderContextChanged; - 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; - public fun equals (Ljava/lang/Object;)Z - public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { public fun ()V public fun (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 public fun toString ()Ljava/lang/String; } -public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { - public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V - public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReconciling; - 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; - public fun equals (Ljava/lang/Object;)Z - public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V 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 } public abstract class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError : java/lang/Exception { + public static final field Companion Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$Companion; public abstract fun errorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; } +public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$Companion { +} + public final class dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError$FlagNotFoundError : dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError { public fun (Ljava/lang/String;Ljava/lang/String;)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt index be5aadd5..33785a01 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt @@ -1,16 +1,8 @@ package dev.openfeature.kotlin.sdk import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents -import dev.openfeature.kotlin.sdk.exceptions.ErrorCode +import dev.openfeature.kotlin.sdk.events.toOpenFeatureStatusError import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError -import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.FlagNotFoundError -import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.GeneralError -import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.InvalidContextError -import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ParseError -import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderFatalError -import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderNotReadyError -import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.TargetingKeyMissingError -import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.TypeMismatchError import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -288,47 +280,11 @@ object OpenFeatureAPI { } is OpenFeatureProviderEvents.ProviderError -> { - val eventDetails = providerEvent.eventDetails - if (eventDetails.errorCode != null) { // priority if EventDetails error has been provided - val openFeatureError = constructOpenFeatureError( - eventDetails.message ?: "Provider did not supply an error message", - errorCode = eventDetails.errorCode - ) - val status = if (eventDetails.errorCode == ErrorCode.PROVIDER_FATAL) { - OpenFeatureStatus.Fatal(openFeatureError) - } else { - OpenFeatureStatus.Error(openFeatureError) - } - _statusFlow.emit(status) - } else if (providerEvent.error != null) { // Deprecated impl - val status = if (providerEvent.error is ProviderFatalError) { - OpenFeatureStatus.Fatal(providerEvent.error) - } else { - OpenFeatureStatus.Error(providerEvent.error) - } - _statusFlow.emit(status) - } else { - _statusFlow.emit( - OpenFeatureStatus.Error(GeneralError("Unknown error")) - ) - } + _statusFlow.emit(providerEvent.toOpenFeatureStatusError()) } else -> { // All other states should not be emitted from here } } } - - private fun constructOpenFeatureError(errorMessage: String, errorCode: ErrorCode): OpenFeatureError { - return when (errorCode) { - ErrorCode.PROVIDER_NOT_READY -> ProviderNotReadyError() - ErrorCode.FLAG_NOT_FOUND -> FlagNotFoundError(flagKey = null, errorMessage) - ErrorCode.PARSE_ERROR -> ParseError(errorMessage) - ErrorCode.TYPE_MISMATCH -> TypeMismatchError(errorMessage) - ErrorCode.TARGETING_KEY_MISSING -> TargetingKeyMissingError(errorMessage) - ErrorCode.INVALID_CONTEXT -> InvalidContextError(errorMessage) - ErrorCode.GENERAL -> GeneralError(errorMessage) - ErrorCode.PROVIDER_FATAL -> ProviderFatalError(errorMessage) - } - } } \ No newline at end of file diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt index 2ba36540..dd754c01 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt @@ -1,7 +1,9 @@ package dev.openfeature.kotlin.sdk.events +import dev.openfeature.kotlin.sdk.OpenFeatureStatus import dev.openfeature.kotlin.sdk.exceptions.ErrorCode import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderFatalError sealed class OpenFeatureProviderEvents { data class EventDetails( @@ -39,22 +41,34 @@ sealed class OpenFeatureProviderEvents { override val eventDetails: EventDetails ) : OpenFeatureProviderEvents() - /** - * The context associated with the provider has changed, and the provider has not yet reconciled its associated state. - */ - data class ProviderReconciling( - override val eventDetails: EventDetails - ) : OpenFeatureProviderEvents() - - /** - * The context associated with the provider has changed, and the provider has reconciled its associated state. - */ - data class ProviderContextChanged( - override val eventDetails: EventDetails - ) : OpenFeatureProviderEvents() - @Deprecated("Use ProviderError instead", ReplaceWith("ProviderError")) data object ProviderNotReady : OpenFeatureProviderEvents() { override val eventDetails = EventDetails() } +} + +internal fun OpenFeatureProviderEvents.ProviderError.toOpenFeatureStatusError(): OpenFeatureStatus { + return when { + eventDetails.errorCode != null -> { + val openFeatureError = OpenFeatureError.fromMessageAndErrorCode( + errorMessage = eventDetails.message ?: "Provider did not supply an error message", + errorCode = eventDetails.errorCode + ) + if (eventDetails.errorCode == ErrorCode.PROVIDER_FATAL) { + OpenFeatureStatus.Fatal(openFeatureError) + } else { + OpenFeatureStatus.Error(openFeatureError) + } + } + + error != null -> { // Deprecated implementation + if (error is ProviderFatalError) { + OpenFeatureStatus.Fatal(error) + } else { + OpenFeatureStatus.Error(error) + } + } + + else -> OpenFeatureStatus.Error(OpenFeatureError.GeneralError("Unspecified error")) + } } \ No newline at end of file diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt index c89991e4..9ecf7b47 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/exceptions/OpenFeatureError.kt @@ -59,4 +59,19 @@ sealed class OpenFeatureError : Exception() { return ErrorCode.PROVIDER_FATAL } } + + companion object { + internal fun fromMessageAndErrorCode(errorMessage: String, errorCode: ErrorCode): OpenFeatureError { + return when (errorCode) { + ErrorCode.PROVIDER_NOT_READY -> ProviderNotReadyError() + ErrorCode.FLAG_NOT_FOUND -> FlagNotFoundError(flagKey = null, errorMessage) + ErrorCode.PARSE_ERROR -> ParseError(errorMessage) + ErrorCode.TYPE_MISMATCH -> TypeMismatchError(errorMessage) + ErrorCode.TARGETING_KEY_MISSING -> TargetingKeyMissingError(errorMessage) + ErrorCode.INVALID_CONTEXT -> InvalidContextError(errorMessage) + ErrorCode.GENERAL -> GeneralError(errorMessage) + ErrorCode.PROVIDER_FATAL -> ProviderFatalError(errorMessage) + } + } + } } \ No newline at end of file diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProvider.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProvider.kt index 30af01ca..b80de7bc 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProvider.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProvider.kt @@ -8,6 +8,7 @@ import dev.openfeature.kotlin.sdk.ProviderEvaluation import dev.openfeature.kotlin.sdk.ProviderMetadata import dev.openfeature.kotlin.sdk.Value import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents +import dev.openfeature.kotlin.sdk.events.toOpenFeatureStatusError import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -158,7 +159,6 @@ class MultiProvider( */ internal fun getProviderCount(): Int = childFeatureProviders.size - // TODO Add distinctUntilChanged operator once EventDetails have been added override fun observe(): Flow = eventFlow.asSharedFlow() /** @@ -197,12 +197,7 @@ class MultiProvider( is OpenFeatureProviderEvents.ProviderReady -> OpenFeatureStatus.Ready is OpenFeatureProviderEvents.ProviderNotReady -> OpenFeatureStatus.NotReady is OpenFeatureProviderEvents.ProviderStale -> OpenFeatureStatus.Stale - is OpenFeatureProviderEvents.ProviderError -> - if (event.error is OpenFeatureError.ProviderFatalError) { - OpenFeatureStatus.Fatal(event.error) - } else { - OpenFeatureStatus.Error(event.error) - } + is OpenFeatureProviderEvents.ProviderError -> event.toOpenFeatureStatusError() } val previousStatus = _statusFlow.value diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/EventDetailsTests.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/EventDetailsTests.kt new file mode 100644 index 00000000..1db56c73 --- /dev/null +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/EventDetailsTests.kt @@ -0,0 +1,75 @@ +package dev.openfeature.kotlin.sdk + +import dev.openfeature.kotlin.sdk.events.OpenFeatureProviderEvents +import dev.openfeature.kotlin.sdk.events.toOpenFeatureStatusError +import dev.openfeature.kotlin.sdk.exceptions.ErrorCode +import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs + +class EventDetailsTests { + + @Test + fun providerErrorEventDetailsMapToFatal() { + val evt = OpenFeatureProviderEvents.ProviderError( + OpenFeatureProviderEvents.EventDetails( + message = "message", + errorCode = ErrorCode.PROVIDER_FATAL + ) + ) + + val status = evt.toOpenFeatureStatusError() + val fatal = assertIs(status) + val err = assertIs(fatal.error) + assertEquals("message", err.message) + } + + @Test + fun providerErrorEventDetailsMapToError() { + val evt = OpenFeatureProviderEvents.ProviderError( + OpenFeatureProviderEvents.EventDetails( + message = "flag missing", + errorCode = ErrorCode.FLAG_NOT_FOUND + ) + ) + + val status = evt.toOpenFeatureStatusError() + val error = assertIs(status) + assertIs(error.error) + assertEquals("flag missing", error.error.message) + } + + @Test + fun providerErrorMapToFatal() { + val evt = OpenFeatureProviderEvents.ProviderError( + error = OpenFeatureError.ProviderFatalError("message") + ) + + val status = evt.toOpenFeatureStatusError() + val fatal = assertIs(status) + val err = assertIs(fatal.error) + assertEquals("message", err.message) + } + + @Test + fun providerErrorMapToError() { + val evt = OpenFeatureProviderEvents.ProviderError( + error = OpenFeatureError.InvalidContextError("message") + ) + + val status = evt.toOpenFeatureStatusError() + val fatal = assertIs(status) + val err = assertIs(fatal.error) + assertEquals("message", err.message) + } + + @Test + fun providerErrorMapToUnspecifiedError() { + val evt = OpenFeatureProviderEvents.ProviderError() + + val status = evt.toOpenFeatureStatusError() + val fatal = assertIs(status) + assertIs(fatal.error) + } +} \ No newline at end of file diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProviderTests.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProviderTests.kt index 60eec8dd..748b4885 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProviderTests.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProviderTests.kt @@ -106,9 +106,9 @@ class MultiProviderTests { val provider = FakeEventProvider( name = "p", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderReady, - OpenFeatureProviderEvents.ProviderStale + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) ) ) val multi = MultiProvider(listOf(provider)) @@ -118,7 +118,7 @@ class MultiProviderTests { // The last emitted event should be STALE given the sequence above val last = multi.observe().first() - assertEquals(OpenFeatureProviderEvents.ProviderStale, last) + assertEquals(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), last) initJob.cancelAndJoin() } @@ -143,24 +143,27 @@ class MultiProviderTests { val a = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderReady + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()) ) ) val b = FakeEventProvider( name = "B", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderStale + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) ) ) val c = FakeEventProvider( name = "C", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), OpenFeatureProviderEvents.ProviderNotReady, OpenFeatureProviderEvents.ProviderError( - OpenFeatureError.GeneralError("boom") + OpenFeatureProviderEvents.EventDetails( + message = "boom", + errorCode = dev.openfeature.kotlin.sdk.exceptions.ErrorCode.GENERAL + ) ) ) ) @@ -180,16 +183,19 @@ class MultiProviderTests { val a = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderReady + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()) ) ) val b = FakeEventProvider( name = "B", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), OpenFeatureProviderEvents.ProviderError( - OpenFeatureError.ProviderFatalError("fatal") + OpenFeatureProviderEvents.EventDetails( + message = "fatal", + errorCode = dev.openfeature.kotlin.sdk.exceptions.ErrorCode.PROVIDER_FATAL + ) ) ) ) @@ -209,24 +215,27 @@ class MultiProviderTests { val a = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderReady + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()) ) ) val b = FakeEventProvider( name = "B", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), OpenFeatureProviderEvents.ProviderError( - OpenFeatureError.GeneralError("oops") + OpenFeatureProviderEvents.EventDetails( + message = "oops", + errorCode = dev.openfeature.kotlin.sdk.exceptions.ErrorCode.GENERAL + ) ) ) ) val c = FakeEventProvider( name = "C", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderStale + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) ) ) @@ -245,22 +254,27 @@ class MultiProviderTests { val a = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), OpenFeatureProviderEvents.ProviderNotReady ) ) val b = FakeEventProvider( name = "B", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderError(OpenFeatureError.GeneralError("e")) + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderError( + OpenFeatureProviderEvents.EventDetails( + message = "e", + errorCode = dev.openfeature.kotlin.sdk.exceptions.ErrorCode.GENERAL + ) + ) ) ) val c = FakeEventProvider( name = "C", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderStale + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) ) ) val multi = MultiProvider(listOf(a, b, c)) @@ -278,9 +292,9 @@ class MultiProviderTests { val provider = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderReady, - OpenFeatureProviderEvents.ProviderReady, - OpenFeatureProviderEvents.ProviderStale + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) ) ) val multi = MultiProvider(listOf(provider)) @@ -297,7 +311,10 @@ class MultiProviderTests { val nonConfig = collected.filter { it !is OpenFeatureProviderEvents.ProviderConfigurationChanged } // Should only emit Ready once (transition) and Stale once (transition) assertEquals( - listOf(OpenFeatureProviderEvents.ProviderReady, OpenFeatureProviderEvents.ProviderStale), + listOf( + OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) + ), nonConfig ) } @@ -307,8 +324,8 @@ class MultiProviderTests { val provider = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged, - OpenFeatureProviderEvents.ProviderConfigurationChanged + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()) ) ) val multi = MultiProvider(listOf(provider)) From 3b317467adeb1478ccf15c00bdfb861e9b2e492c Mon Sep 17 00:00:00 2001 From: penguindan Date: Fri, 5 Sep 2025 14:04:50 -0700 Subject: [PATCH 3/7] Actually deprecate Signed-off-by: penguindan --- .../kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt index 33785a01..a0acc14a 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt @@ -45,6 +45,7 @@ object OpenFeatureAPI { * Deprecated because calls to this should move to OpenFeatureClient according to * https://openfeature.dev/specification/sections/flag-evaluation#17-provider-lifecycle-management */ + @Deprecated("Please use OpenFeatureClient.providerStatusFlow instead") val statusFlow: Flow get() = _statusFlow.distinctUntilChanged() var hooks: List> = listOf() From fa6b87c8ee67fe84d420b5c46f51717bb04a264b Mon Sep 17 00:00:00 2001 From: penguindan Date: Mon, 8 Sep 2025 10:20:54 -0700 Subject: [PATCH 4/7] Default value of EventDetails is null to signify undefined Signed-off-by: penguindan --- kotlin-sdk/api/android/kotlin-sdk.api | 9 ++- kotlin-sdk/api/jvm/kotlin-sdk.api | 9 ++- .../sdk/events/OpenFeatureProviderEvents.kt | 14 ++--- .../kotlin/sdk/DeveloperExperienceTests.kt | 8 +-- .../kotlin/sdk/ProviderEventingTests.kt | 28 +++++----- .../kotlin/sdk/helpers/AutoHealingProvider.kt | 2 +- .../kotlin/sdk/helpers/DoSomethingProvider.kt | 14 ++--- .../sdk/multiprovider/MultiProviderTests.kt | 56 +++++++++---------- 8 files changed, 76 insertions(+), 64 deletions(-) diff --git a/kotlin-sdk/api/android/kotlin-sdk.api b/kotlin-sdk/api/android/kotlin-sdk.api index 57c0e178..202da96c 100644 --- a/kotlin-sdk/api/android/kotlin-sdk.api +++ b/kotlin-sdk/api/android/kotlin-sdk.api @@ -664,7 +664,9 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$E } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun ()V public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public synthetic fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; @@ -692,13 +694,16 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady; public fun equals (Ljava/lang/Object;)Z - public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public synthetic fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun getEventDetails ()Ljava/lang/Void; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun ()V public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public synthetic fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; @@ -709,7 +714,9 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun ()V public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public synthetic fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; diff --git a/kotlin-sdk/api/jvm/kotlin-sdk.api b/kotlin-sdk/api/jvm/kotlin-sdk.api index 57c0e178..202da96c 100644 --- a/kotlin-sdk/api/jvm/kotlin-sdk.api +++ b/kotlin-sdk/api/jvm/kotlin-sdk.api @@ -664,7 +664,9 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$E } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun ()V public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public synthetic fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderConfigurationChanged; @@ -692,13 +694,16 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady; public fun equals (Ljava/lang/Object;)Z - public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public synthetic fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public fun getEventDetails ()Ljava/lang/Void; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun ()V public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public synthetic fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady; @@ -709,7 +714,9 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P } public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents { + public fun ()V public fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V + public synthetic fun (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderStale; diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt index dd754c01..58604ef6 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt @@ -13,43 +13,43 @@ sealed class OpenFeatureProviderEvents { val eventMetadata: Map = emptyMap() ) - abstract val eventDetails: EventDetails + abstract val eventDetails: EventDetails? /** * The provider is ready to perform flag evaluations. */ data class ProviderReady( - override val eventDetails: EventDetails + override val eventDetails: EventDetails? = null ) : OpenFeatureProviderEvents() /** * The provider signaled an error. */ data class ProviderError( - override val eventDetails: EventDetails = EventDetails(), + override val eventDetails: EventDetails? = null, @Deprecated("Please use eventDetails instead.") val error: OpenFeatureError? = null ) : OpenFeatureProviderEvents() data class ProviderConfigurationChanged( - override val eventDetails: EventDetails + override val eventDetails: EventDetails? = null ) : OpenFeatureProviderEvents() /** * The provider's cached state is no longer valid and may not be up-to-date with the source of truth. */ data class ProviderStale( - override val eventDetails: EventDetails + override val eventDetails: EventDetails? = null ) : OpenFeatureProviderEvents() @Deprecated("Use ProviderError instead", ReplaceWith("ProviderError")) data object ProviderNotReady : OpenFeatureProviderEvents() { - override val eventDetails = EventDetails() + override val eventDetails = null } } internal fun OpenFeatureProviderEvents.ProviderError.toOpenFeatureStatusError(): OpenFeatureStatus { return when { - eventDetails.errorCode != null -> { + eventDetails?.errorCode != null -> { val openFeatureError = OpenFeatureError.fromMessageAndErrorCode( errorMessage = eventDetails.message ?: "Provider did not supply an error message", errorCode = eventDetails.errorCode diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/DeveloperExperienceTests.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/DeveloperExperienceTests.kt index 09fe7636..d4de0194 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/DeveloperExperienceTests.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/DeveloperExperienceTests.kt @@ -351,10 +351,10 @@ class DeveloperExperienceTests { job.cancelAndJoin() assertEquals( listOf( - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderStale() ), staleEvents ) diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/ProviderEventingTests.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/ProviderEventingTests.kt index bace3118..aedabaf4 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/ProviderEventingTests.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/ProviderEventingTests.kt @@ -48,9 +48,7 @@ class ProviderEventingTests { ) delay(healDelayMillis) flow.emit( - OpenFeatureProviderEvents.ProviderConfigurationChanged( - OpenFeatureProviderEvents.EventDetails() - ) + OpenFeatureProviderEvents.ProviderConfigurationChanged() ) } @@ -107,9 +105,9 @@ class ProviderEventingTests { testScheduler.advanceUntilIdle() assertEquals( listOf( - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderReady(), + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderConfigurationChanged() ), emittedEvents ) @@ -131,15 +129,15 @@ class ProviderEventingTests { job.cancelAndJoin() assertEquals( listOf( - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderReady(), + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderReady(), + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderStale(), + OpenFeatureProviderEvents.ProviderConfigurationChanged() ), emittedEvents ) diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/AutoHealingProvider.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/AutoHealingProvider.kt index 34409f13..daa7b9bc 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/AutoHealingProvider.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/AutoHealingProvider.kt @@ -31,7 +31,7 @@ class AutoHealingProvider( ) ) delay(healDelay) - _events.emit(OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails())) + _events.emit(OpenFeatureProviderEvents.ProviderReady()) ready = true } diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/DoSomethingProvider.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/DoSomethingProvider.kt index 185b8eb4..0f57b28b 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/DoSomethingProvider.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/helpers/DoSomethingProvider.kt @@ -27,7 +27,7 @@ open class DoSomethingProvider( override suspend fun initialize(initialContext: EvaluationContext?) { delay(1000) - events.emit(OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails())) + events.emit(OpenFeatureProviderEvents.ProviderReady()) } override fun shutdown() { @@ -39,7 +39,7 @@ open class DoSomethingProvider( newContext: EvaluationContext ) { delay(500) - events.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails())) + events.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged()) } override fun getBooleanEvaluation( @@ -101,8 +101,8 @@ class OverlyEmittingProvider(name: String) : DoSomethingProvider( oldContext: EvaluationContext?, newContext: EvaluationContext ) { - events.emit(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails())) - events.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails())) + events.emit(OpenFeatureProviderEvents.ProviderStale()) + events.emit(OpenFeatureProviderEvents.ProviderConfigurationChanged()) } override fun track( @@ -111,8 +111,8 @@ class OverlyEmittingProvider(name: String) : DoSomethingProvider( details: TrackingEventDetails? ) { super.track(trackingEventName, context, details) - events.tryEmit(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails())) - events.tryEmit(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails())) - events.tryEmit(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails())) + events.tryEmit(OpenFeatureProviderEvents.ProviderStale()) + events.tryEmit(OpenFeatureProviderEvents.ProviderStale()) + events.tryEmit(OpenFeatureProviderEvents.ProviderStale()) } } \ No newline at end of file diff --git a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProviderTests.kt b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProviderTests.kt index 748b4885..e598f8b7 100644 --- a/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProviderTests.kt +++ b/kotlin-sdk/src/commonTest/kotlin/dev/openfeature/kotlin/sdk/multiprovider/MultiProviderTests.kt @@ -106,9 +106,9 @@ class MultiProviderTests { val provider = FakeEventProvider( name = "p", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderReady(), + OpenFeatureProviderEvents.ProviderStale() ) ) val multi = MultiProvider(listOf(provider)) @@ -118,7 +118,7 @@ class MultiProviderTests { // The last emitted event should be STALE given the sequence above val last = multi.observe().first() - assertEquals(OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()), last) + assertEquals(OpenFeatureProviderEvents.ProviderStale(), last) initJob.cancelAndJoin() } @@ -143,21 +143,21 @@ class MultiProviderTests { val a = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderReady() ) ) val b = FakeEventProvider( name = "B", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderStale() ) ) val c = FakeEventProvider( name = "C", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(), OpenFeatureProviderEvents.ProviderNotReady, OpenFeatureProviderEvents.ProviderError( OpenFeatureProviderEvents.EventDetails( @@ -183,14 +183,14 @@ class MultiProviderTests { val a = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderReady() ) ) val b = FakeEventProvider( name = "B", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(), OpenFeatureProviderEvents.ProviderError( OpenFeatureProviderEvents.EventDetails( message = "fatal", @@ -215,14 +215,14 @@ class MultiProviderTests { val a = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderReady() ) ) val b = FakeEventProvider( name = "B", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(), OpenFeatureProviderEvents.ProviderError( OpenFeatureProviderEvents.EventDetails( message = "oops", @@ -234,8 +234,8 @@ class MultiProviderTests { val c = FakeEventProvider( name = "C", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderStale() ) ) @@ -254,14 +254,14 @@ class MultiProviderTests { val a = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(), OpenFeatureProviderEvents.ProviderNotReady ) ) val b = FakeEventProvider( name = "B", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), + OpenFeatureProviderEvents.ProviderConfigurationChanged(), OpenFeatureProviderEvents.ProviderError( OpenFeatureProviderEvents.EventDetails( message = "e", @@ -273,8 +273,8 @@ class MultiProviderTests { val c = FakeEventProvider( name = "C", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderStale() ) ) val multi = MultiProvider(listOf(a, b, c)) @@ -292,9 +292,9 @@ class MultiProviderTests { val provider = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderReady(), + OpenFeatureProviderEvents.ProviderReady(), + OpenFeatureProviderEvents.ProviderStale() ) ) val multi = MultiProvider(listOf(provider)) @@ -312,8 +312,8 @@ class MultiProviderTests { // Should only emit Ready once (transition) and Stale once (transition) assertEquals( listOf( - OpenFeatureProviderEvents.ProviderReady(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderStale(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderReady(), + OpenFeatureProviderEvents.ProviderStale() ), nonConfig ) @@ -324,8 +324,8 @@ class MultiProviderTests { val provider = FakeEventProvider( name = "A", eventsToEmitOnInit = listOf( - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()), - OpenFeatureProviderEvents.ProviderConfigurationChanged(OpenFeatureProviderEvents.EventDetails()) + OpenFeatureProviderEvents.ProviderConfigurationChanged(), + OpenFeatureProviderEvents.ProviderConfigurationChanged() ) ) val multi = MultiProvider(listOf(provider)) From 534f35e7cd2f2d4b0418ac9242a9d3f8dc197f94 Mon Sep 17 00:00:00 2001 From: penguindan Date: Mon, 8 Sep 2025 14:21:35 -0700 Subject: [PATCH 5/7] Changed list should be a set Signed-off-by: penguindan --- kotlin-sdk/api/android/kotlin-sdk.api | 12 ++++++------ kotlin-sdk/api/jvm/kotlin-sdk.api | 12 ++++++------ .../kotlin/sdk/events/OpenFeatureProviderEvents.kt | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/kotlin-sdk/api/android/kotlin-sdk.api b/kotlin-sdk/api/android/kotlin-sdk.api index 202da96c..59788d1e 100644 --- a/kotlin-sdk/api/android/kotlin-sdk.api +++ b/kotlin-sdk/api/android/kotlin-sdk.api @@ -646,18 +646,18 @@ public abstract class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvent public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails { public fun ()V - public fun (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)V - public synthetic fun (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/List; + public fun (Ljava/util/Set;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)V + public synthetic fun (Ljava/util/Set;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Set; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; public final fun component4 ()Ljava/util/Map; - public final fun copy (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ljava/util/Set;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ljava/util/Set;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public fun equals (Ljava/lang/Object;)Z public final fun getErrorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; public final fun getEventMetadata ()Ljava/util/Map; - public final fun getFlagsChanged ()Ljava/util/List; + public final fun getFlagsChanged ()Ljava/util/Set; public final fun getMessage ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; diff --git a/kotlin-sdk/api/jvm/kotlin-sdk.api b/kotlin-sdk/api/jvm/kotlin-sdk.api index 202da96c..59788d1e 100644 --- a/kotlin-sdk/api/jvm/kotlin-sdk.api +++ b/kotlin-sdk/api/jvm/kotlin-sdk.api @@ -646,18 +646,18 @@ public abstract class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvent public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails { public fun ()V - public fun (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)V - public synthetic fun (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/List; + public fun (Ljava/util/Set;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)V + public synthetic fun (Ljava/util/Set;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/Set; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; public final fun component4 ()Ljava/util/Map; - public final fun copy (Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; - public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ljava/util/List;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public final fun copy (Ljava/util/Set;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; + public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ljava/util/Set;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode;Ljava/util/Map;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails; public fun equals (Ljava/lang/Object;)Z public final fun getErrorCode ()Ldev/openfeature/kotlin/sdk/exceptions/ErrorCode; public final fun getEventMetadata ()Ljava/util/Map; - public final fun getFlagsChanged ()Ljava/util/List; + public final fun getFlagsChanged ()Ljava/util/Set; public final fun getMessage ()Ljava/lang/String; public fun hashCode ()I public fun toString ()Ljava/lang/String; diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt index 58604ef6..a121c9d2 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents.kt @@ -7,7 +7,7 @@ import dev.openfeature.kotlin.sdk.exceptions.OpenFeatureError.ProviderFatalError sealed class OpenFeatureProviderEvents { data class EventDetails( - val flagsChanged: List = emptyList(), + val flagsChanged: Set = emptySet(), val message: String? = null, val errorCode: ErrorCode? = null, val eventMetadata: Map = emptyMap() From d69c92b1ad0c521302e2c492620e34b07ed86849 Mon Sep 17 00:00:00 2001 From: penguindan Date: Tue, 9 Sep 2025 16:37:46 -0700 Subject: [PATCH 6/7] Add providerStatusFlow as part of the Client interface Signed-off-by: penguindan --- kotlin-sdk/api/android/kotlin-sdk.api | 3 ++- kotlin-sdk/api/jvm/kotlin-sdk.api | 3 ++- .../src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt | 3 +++ .../kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/kotlin-sdk/api/android/kotlin-sdk.api b/kotlin-sdk/api/android/kotlin-sdk.api index 59788d1e..66304d60 100644 --- a/kotlin-sdk/api/android/kotlin-sdk.api +++ b/kotlin-sdk/api/android/kotlin-sdk.api @@ -19,6 +19,7 @@ public abstract interface class dev/openfeature/kotlin/sdk/Client : dev/openfeat public abstract fun addHooks (Ljava/util/List;)V public abstract fun getHooks ()Ljava/util/List; public abstract fun getMetadata ()Ldev/openfeature/kotlin/sdk/ClientMetadata; + public abstract fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; } public abstract interface class dev/openfeature/kotlin/sdk/ClientMetadata { @@ -312,7 +313,7 @@ public final class dev/openfeature/kotlin/sdk/OpenFeatureClient : dev/openfeatur public fun getObjectDetails (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;)Ldev/openfeature/kotlin/sdk/Value; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/Value; - public final fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; + public fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringValue (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; diff --git a/kotlin-sdk/api/jvm/kotlin-sdk.api b/kotlin-sdk/api/jvm/kotlin-sdk.api index 59788d1e..66304d60 100644 --- a/kotlin-sdk/api/jvm/kotlin-sdk.api +++ b/kotlin-sdk/api/jvm/kotlin-sdk.api @@ -19,6 +19,7 @@ public abstract interface class dev/openfeature/kotlin/sdk/Client : dev/openfeat public abstract fun addHooks (Ljava/util/List;)V public abstract fun getHooks ()Ljava/util/List; public abstract fun getMetadata ()Ldev/openfeature/kotlin/sdk/ClientMetadata; + public abstract fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; } public abstract interface class dev/openfeature/kotlin/sdk/ClientMetadata { @@ -312,7 +313,7 @@ public final class dev/openfeature/kotlin/sdk/OpenFeatureClient : dev/openfeatur public fun getObjectDetails (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;)Ldev/openfeature/kotlin/sdk/Value; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/Value; - public final fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; + public fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringValue (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt index 4da16a58..5a6833df 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt @@ -1,8 +1,11 @@ package dev.openfeature.kotlin.sdk +import kotlinx.coroutines.flow.Flow + interface Client : Features, Tracking { val metadata: ClientMetadata val hooks: List> + val providerStatusFlow: Flow fun addHooks(hooks: List>) } \ No newline at end of file diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt index 102040cd..d1791ced 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt @@ -29,7 +29,7 @@ class OpenFeatureClient( * https://openfeature.dev/specification/sections/flag-evaluation#17-provider-lifecycle-management */ @Suppress("DEPRECATION") - val providerStatusFlow = openFeatureAPI.statusFlow + override val providerStatusFlow = openFeatureAPI.statusFlow override fun getBooleanValue(key: String, defaultValue: Boolean): Boolean { return getBooleanDetails(key, defaultValue).value From 6a2b8ad3d6a529577a55169308828f8917075631 Mon Sep 17 00:00:00 2001 From: penguindan Date: Mon, 15 Sep 2025 11:07:26 -0700 Subject: [PATCH 7/7] Remove deprecatin warnings and update status flow naming Signed-off-by: penguindan --- kotlin-sdk/api/android/kotlin-sdk.api | 4 ++-- kotlin-sdk/api/jvm/kotlin-sdk.api | 4 ++-- .../commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt | 2 +- .../kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt | 7 ------- .../kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt | 7 +------ 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/kotlin-sdk/api/android/kotlin-sdk.api b/kotlin-sdk/api/android/kotlin-sdk.api index 66304d60..d1dea7be 100644 --- a/kotlin-sdk/api/android/kotlin-sdk.api +++ b/kotlin-sdk/api/android/kotlin-sdk.api @@ -19,7 +19,7 @@ public abstract interface class dev/openfeature/kotlin/sdk/Client : dev/openfeat public abstract fun addHooks (Ljava/util/List;)V public abstract fun getHooks ()Ljava/util/List; public abstract fun getMetadata ()Ldev/openfeature/kotlin/sdk/ClientMetadata; - public abstract fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; + public abstract fun getStatusFlow ()Lkotlinx/coroutines/flow/Flow; } public abstract interface class dev/openfeature/kotlin/sdk/ClientMetadata { @@ -313,7 +313,7 @@ public final class dev/openfeature/kotlin/sdk/OpenFeatureClient : dev/openfeatur public fun getObjectDetails (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;)Ldev/openfeature/kotlin/sdk/Value; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/Value; - public fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; + public fun getStatusFlow ()Lkotlinx/coroutines/flow/Flow; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringValue (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; diff --git a/kotlin-sdk/api/jvm/kotlin-sdk.api b/kotlin-sdk/api/jvm/kotlin-sdk.api index 66304d60..d1dea7be 100644 --- a/kotlin-sdk/api/jvm/kotlin-sdk.api +++ b/kotlin-sdk/api/jvm/kotlin-sdk.api @@ -19,7 +19,7 @@ public abstract interface class dev/openfeature/kotlin/sdk/Client : dev/openfeat public abstract fun addHooks (Ljava/util/List;)V public abstract fun getHooks ()Ljava/util/List; public abstract fun getMetadata ()Ldev/openfeature/kotlin/sdk/ClientMetadata; - public abstract fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; + public abstract fun getStatusFlow ()Lkotlinx/coroutines/flow/Flow; } public abstract interface class dev/openfeature/kotlin/sdk/ClientMetadata { @@ -313,7 +313,7 @@ public final class dev/openfeature/kotlin/sdk/OpenFeatureClient : dev/openfeatur public fun getObjectDetails (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;)Ldev/openfeature/kotlin/sdk/Value; public fun getObjectValue (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/Value; - public fun getProviderStatusFlow ()Lkotlinx/coroutines/flow/Flow; + public fun getStatusFlow ()Lkotlinx/coroutines/flow/Flow; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringDetails (Ljava/lang/String;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/FlagEvaluationOptions;)Ldev/openfeature/kotlin/sdk/FlagEvaluationDetails; public fun getStringValue (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt index 5a6833df..13aefdf2 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/Client.kt @@ -5,7 +5,7 @@ import kotlinx.coroutines.flow.Flow interface Client : Features, Tracking { val metadata: ClientMetadata val hooks: List> - val providerStatusFlow: Flow + val statusFlow: Flow fun addHooks(hooks: List>) } \ No newline at end of file diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt index a0acc14a..36d7f583 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureAPI.kt @@ -39,13 +39,6 @@ object OpenFeatureAPI { tryEmit(OpenFeatureStatus.NotReady) } - /** - * A flow of [OpenFeatureStatus] that emits the current status of the SDK. - * - * Deprecated because calls to this should move to OpenFeatureClient according to - * https://openfeature.dev/specification/sections/flag-evaluation#17-provider-lifecycle-management - */ - @Deprecated("Please use OpenFeatureClient.providerStatusFlow instead") val statusFlow: Flow get() = _statusFlow.distinctUntilChanged() var hooks: List> = listOf() diff --git a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt index d1791ced..b6f5eb10 100644 --- a/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt +++ b/kotlin-sdk/src/commonMain/kotlin/dev/openfeature/kotlin/sdk/OpenFeatureClient.kt @@ -24,12 +24,7 @@ class OpenFeatureClient( this.hooks += hooks } - /** - * Calls from OpenFeatureAPI.statusFlow should move to this client according to - * https://openfeature.dev/specification/sections/flag-evaluation#17-provider-lifecycle-management - */ - @Suppress("DEPRECATION") - override val providerStatusFlow = openFeatureAPI.statusFlow + override val statusFlow = openFeatureAPI.statusFlow override fun getBooleanValue(key: String, defaultValue: Boolean): Boolean { return getBooleanDetails(key, defaultValue).value