Skip to content

Commit eaa5335

Browse files
committed
feat: In-memory provider, gherkin spec test (#208)
Signed-off-by: Marcin Stepien <marcin.stepien@fluxon.com>
1 parent d11b0d3 commit eaa5335

9 files changed

Lines changed: 1105 additions & 1 deletion

File tree

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,3 +861,68 @@ public abstract interface class dev/openfeature/kotlin/sdk/multiprovider/MultiPr
861861
public abstract fun evaluate (Ljava/util/List;Ljava/lang/String;Ljava/lang/Object;Ldev/openfeature/kotlin/sdk/EvaluationContext;Lkotlin/jvm/functions/Function4;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
862862
}
863863

864+
public abstract interface class dev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator {
865+
public abstract fun evaluate (Ldev/openfeature/kotlin/sdk/providers/memory/Flag;Ldev/openfeature/kotlin/sdk/EvaluationContext;)Ljava/lang/String;
866+
}
867+
868+
public final class dev/openfeature/kotlin/sdk/providers/memory/Flag {
869+
public static final field Companion Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Companion;
870+
public fun <init> (Ljava/util/Map;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;Ldev/openfeature/kotlin/sdk/EvaluationMetadata;Z)V
871+
public synthetic fun <init> (Ljava/util/Map;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;Ldev/openfeature/kotlin/sdk/EvaluationMetadata;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
872+
public final fun component1 ()Ljava/util/Map;
873+
public final fun component2 ()Ljava/lang/String;
874+
public final fun component3 ()Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;
875+
public final fun component4 ()Ldev/openfeature/kotlin/sdk/EvaluationMetadata;
876+
public final fun component5 ()Z
877+
public final fun copy (Ljava/util/Map;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;Ldev/openfeature/kotlin/sdk/EvaluationMetadata;Z)Ldev/openfeature/kotlin/sdk/providers/memory/Flag;
878+
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/providers/memory/Flag;Ljava/util/Map;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;Ldev/openfeature/kotlin/sdk/EvaluationMetadata;ZILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag;
879+
public fun equals (Ljava/lang/Object;)Z
880+
public final fun getContextEvaluator ()Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;
881+
public final fun getDefaultVariant ()Ljava/lang/String;
882+
public final fun getDisabled ()Z
883+
public final fun getFlagMetadata ()Ldev/openfeature/kotlin/sdk/EvaluationMetadata;
884+
public final fun getVariants ()Ljava/util/Map;
885+
public fun hashCode ()I
886+
public fun toString ()Ljava/lang/String;
887+
}
888+
889+
public final class dev/openfeature/kotlin/sdk/providers/memory/Flag$Builder {
890+
public fun <init> ()V
891+
public final fun build ()Ldev/openfeature/kotlin/sdk/providers/memory/Flag;
892+
public final fun contextEvaluator (Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
893+
public final fun defaultVariant (Ljava/lang/String;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
894+
public final fun disabled (Z)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
895+
public final fun flagMetadata (Ldev/openfeature/kotlin/sdk/EvaluationMetadata;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
896+
public final fun variant (Ljava/lang/String;Ljava/lang/Object;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
897+
public final fun variants (Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
898+
}
899+
900+
public final class dev/openfeature/kotlin/sdk/providers/memory/Flag$Companion {
901+
public final fun builder ()Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
902+
}
903+
904+
public final class dev/openfeature/kotlin/sdk/providers/memory/InMemoryProvider : dev/openfeature/kotlin/sdk/FeatureProvider {
905+
public fun <init> ()V
906+
public fun <init> (Ljava/util/Map;)V
907+
public synthetic fun <init> (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
908+
public fun getBooleanEvaluation (Ljava/lang/String;ZLdev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
909+
public fun getDoubleEvaluation (Ljava/lang/String;DLdev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
910+
public fun getHooks ()Ljava/util/List;
911+
public fun getIntegerEvaluation (Ljava/lang/String;ILdev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
912+
public fun getMetadata ()Ldev/openfeature/kotlin/sdk/ProviderMetadata;
913+
public fun getObjectEvaluation (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
914+
public fun getStringEvaluation (Ljava/lang/String;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
915+
public fun initialize (Ldev/openfeature/kotlin/sdk/EvaluationContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
916+
public fun observe ()Lkotlinx/coroutines/flow/Flow;
917+
public fun onContextSet (Ldev/openfeature/kotlin/sdk/EvaluationContext;Ldev/openfeature/kotlin/sdk/EvaluationContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
918+
public fun shutdown ()V
919+
public fun track (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/EvaluationContext;Ldev/openfeature/kotlin/sdk/TrackingEventDetails;)V
920+
public final fun updateFlags (Ljava/util/Map;)V
921+
}
922+
923+
public final class dev/openfeature/kotlin/sdk/providers/memory/InMemoryProvider$InMemoryProviderMetadata : dev/openfeature/kotlin/sdk/ProviderMetadata {
924+
public fun <init> (Ljava/lang/String;)V
925+
public fun getName ()Ljava/lang/String;
926+
public fun getOriginalMetadata ()Ljava/util/Map;
927+
}
928+

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

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,3 +861,68 @@ public abstract interface class dev/openfeature/kotlin/sdk/multiprovider/MultiPr
861861
public abstract fun evaluate (Ljava/util/List;Ljava/lang/String;Ljava/lang/Object;Ldev/openfeature/kotlin/sdk/EvaluationContext;Lkotlin/jvm/functions/Function4;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
862862
}
863863

864+
public abstract interface class dev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator {
865+
public abstract fun evaluate (Ldev/openfeature/kotlin/sdk/providers/memory/Flag;Ldev/openfeature/kotlin/sdk/EvaluationContext;)Ljava/lang/String;
866+
}
867+
868+
public final class dev/openfeature/kotlin/sdk/providers/memory/Flag {
869+
public static final field Companion Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Companion;
870+
public fun <init> (Ljava/util/Map;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;Ldev/openfeature/kotlin/sdk/EvaluationMetadata;Z)V
871+
public synthetic fun <init> (Ljava/util/Map;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;Ldev/openfeature/kotlin/sdk/EvaluationMetadata;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
872+
public final fun component1 ()Ljava/util/Map;
873+
public final fun component2 ()Ljava/lang/String;
874+
public final fun component3 ()Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;
875+
public final fun component4 ()Ldev/openfeature/kotlin/sdk/EvaluationMetadata;
876+
public final fun component5 ()Z
877+
public final fun copy (Ljava/util/Map;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;Ldev/openfeature/kotlin/sdk/EvaluationMetadata;Z)Ldev/openfeature/kotlin/sdk/providers/memory/Flag;
878+
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/providers/memory/Flag;Ljava/util/Map;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;Ldev/openfeature/kotlin/sdk/EvaluationMetadata;ZILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag;
879+
public fun equals (Ljava/lang/Object;)Z
880+
public final fun getContextEvaluator ()Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;
881+
public final fun getDefaultVariant ()Ljava/lang/String;
882+
public final fun getDisabled ()Z
883+
public final fun getFlagMetadata ()Ldev/openfeature/kotlin/sdk/EvaluationMetadata;
884+
public final fun getVariants ()Ljava/util/Map;
885+
public fun hashCode ()I
886+
public fun toString ()Ljava/lang/String;
887+
}
888+
889+
public final class dev/openfeature/kotlin/sdk/providers/memory/Flag$Builder {
890+
public fun <init> ()V
891+
public final fun build ()Ldev/openfeature/kotlin/sdk/providers/memory/Flag;
892+
public final fun contextEvaluator (Ldev/openfeature/kotlin/sdk/providers/memory/ContextEvaluator;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
893+
public final fun defaultVariant (Ljava/lang/String;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
894+
public final fun disabled (Z)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
895+
public final fun flagMetadata (Ldev/openfeature/kotlin/sdk/EvaluationMetadata;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
896+
public final fun variant (Ljava/lang/String;Ljava/lang/Object;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
897+
public final fun variants (Ljava/util/Map;)Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
898+
}
899+
900+
public final class dev/openfeature/kotlin/sdk/providers/memory/Flag$Companion {
901+
public final fun builder ()Ldev/openfeature/kotlin/sdk/providers/memory/Flag$Builder;
902+
}
903+
904+
public final class dev/openfeature/kotlin/sdk/providers/memory/InMemoryProvider : dev/openfeature/kotlin/sdk/FeatureProvider {
905+
public fun <init> ()V
906+
public fun <init> (Ljava/util/Map;)V
907+
public synthetic fun <init> (Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
908+
public fun getBooleanEvaluation (Ljava/lang/String;ZLdev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
909+
public fun getDoubleEvaluation (Ljava/lang/String;DLdev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
910+
public fun getHooks ()Ljava/util/List;
911+
public fun getIntegerEvaluation (Ljava/lang/String;ILdev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
912+
public fun getMetadata ()Ldev/openfeature/kotlin/sdk/ProviderMetadata;
913+
public fun getObjectEvaluation (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/Value;Ldev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
914+
public fun getStringEvaluation (Ljava/lang/String;Ljava/lang/String;Ldev/openfeature/kotlin/sdk/EvaluationContext;)Ldev/openfeature/kotlin/sdk/ProviderEvaluation;
915+
public fun initialize (Ldev/openfeature/kotlin/sdk/EvaluationContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
916+
public fun observe ()Lkotlinx/coroutines/flow/Flow;
917+
public fun onContextSet (Ldev/openfeature/kotlin/sdk/EvaluationContext;Ldev/openfeature/kotlin/sdk/EvaluationContext;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
918+
public fun shutdown ()V
919+
public fun track (Ljava/lang/String;Ldev/openfeature/kotlin/sdk/EvaluationContext;Ldev/openfeature/kotlin/sdk/TrackingEventDetails;)V
920+
public final fun updateFlags (Ljava/util/Map;)V
921+
}
922+
923+
public final class dev/openfeature/kotlin/sdk/providers/memory/InMemoryProvider$InMemoryProviderMetadata : dev/openfeature/kotlin/sdk/ProviderMetadata {
924+
public fun <init> (Ljava/lang/String;)V
925+
public fun getName ()Ljava/lang/String;
926+
public fun getOriginalMetadata ()Ljava/util/Map;
927+
}
928+

kotlin-sdk/build.gradle.kts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ kotlin {
3838
}
3939
}
4040
}
41+
testRuns["test"].executionTask.configure {
42+
useJUnitPlatform()
43+
}
4144
}
4245
linuxX64 {}
4346
js {
@@ -59,11 +62,19 @@ kotlin {
5962
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.4.0")
6063
}
6164
commonTest.dependencies {
62-
implementation("org.jetbrains.kotlin:kotlin-test:2.2.10")
65+
implementation(kotlin("test"))
6366
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
6467
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-debug:1.7.3")
6568
implementation("org.jetbrains.kotlinx:atomicfu:0.29.0")
6669
}
70+
val jvmTest by getting {
71+
resources.srcDir("../spec/specification/assets/gherkin")
72+
dependencies {
73+
implementation("org.junit.platform:junit-platform-suite:1.10.1")
74+
implementation("io.cucumber:cucumber-java:7.15.0")
75+
implementation("io.cucumber:cucumber-junit-platform-engine:7.15.0")
76+
}
77+
}
6778
}
6879
}
6980

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package dev.openfeature.kotlin.sdk.providers.memory
2+
3+
import dev.openfeature.kotlin.sdk.EvaluationContext
4+
5+
/**
6+
* Context evaluator - use for resolving flag according to evaluation context, for handling targeting.
7+
*/
8+
fun interface ContextEvaluator<T> {
9+
/**
10+
* Evaluates the flag's specific variant based on the provided evaluation context.
11+
*
12+
* @param flag the feature flag representation
13+
* @param evaluationContext the context used for targeting
14+
* @return the resolved variant key (a string matching a key in the flag's variants map),
15+
* or null if no match was found.
16+
*/
17+
fun evaluate(flag: Flag<T>, evaluationContext: EvaluationContext?): String?
18+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package dev.openfeature.kotlin.sdk.providers.memory
2+
3+
import dev.openfeature.kotlin.sdk.EvaluationMetadata
4+
5+
/**
6+
* Flag representation for the in-memory provider.
7+
*/
8+
data class Flag<T>(
9+
val variants: Map<String, T>,
10+
val defaultVariant: String? = null,
11+
val contextEvaluator: ContextEvaluator<T>? = null,
12+
val flagMetadata: EvaluationMetadata? = null,
13+
val disabled: Boolean = false
14+
) {
15+
init {
16+
if (defaultVariant != null && !variants.containsKey(defaultVariant)) {
17+
throw IllegalArgumentException("defaultVariant ($defaultVariant) is not present in variants map")
18+
}
19+
}
20+
21+
companion object {
22+
fun <T> builder() = Builder<T>()
23+
}
24+
25+
class Builder<T> {
26+
private val variants = mutableMapOf<String, T>()
27+
private var defaultVariant: String? = null
28+
private var contextEvaluator: ContextEvaluator<T>? = null
29+
private var flagMetadata: EvaluationMetadata? = null
30+
private var disabled: Boolean = false
31+
32+
fun variant(name: String, value: T) = apply { this.variants[name] = value }
33+
fun variants(variants: Map<String, T>) = apply { this.variants.putAll(variants) }
34+
fun defaultVariant(defaultVariant: String?) = apply { this.defaultVariant = defaultVariant }
35+
fun contextEvaluator(contextEvaluator: ContextEvaluator<T>) = apply { this.contextEvaluator = contextEvaluator }
36+
fun flagMetadata(flagMetadata: EvaluationMetadata) = apply { this.flagMetadata = flagMetadata }
37+
fun disabled(disabled: Boolean) = apply { this.disabled = disabled }
38+
39+
fun build(): Flag<T> {
40+
return Flag(variants.toMap(), defaultVariant, contextEvaluator, flagMetadata, disabled)
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)