Skip to content

Commit 6e9aa29

Browse files
authored
feat: add flag evaluation metadata to evaluation details (#111)
Signed-off-by: Nicklas Lundin <[email protected]> Signed-off-by: Nicklas Lundin <[email protected]>
1 parent 0421d8e commit 6e9aa29

File tree

6 files changed

+137
-15
lines changed

6 files changed

+137
-15
lines changed

android/src/main/java/dev/openfeature/sdk/FlagEvaluationDetails.kt

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import dev.openfeature.sdk.exceptions.ErrorCode
44

55
data class FlagEvaluationDetails<T>(
66
val flagKey: String,
7-
override val value: T,
8-
override val variant: String? = null,
9-
override val reason: String? = null,
10-
override val errorCode: ErrorCode? = null,
11-
override val errorMessage: String? = null
12-
) : BaseEvaluation<T> {
7+
val value: T,
8+
val variant: String? = null,
9+
val reason: String? = null,
10+
val errorCode: ErrorCode? = null,
11+
val errorMessage: String? = null,
12+
val metadata: EvaluationMetadata = EvaluationMetadata.EMPTY
13+
) {
1314
companion object
1415
}
1516

@@ -18,11 +19,12 @@ fun <T> FlagEvaluationDetails.Companion.from(
1819
flagKey: String
1920
): FlagEvaluationDetails<T> {
2021
return FlagEvaluationDetails(
21-
flagKey,
22-
providerEval.value,
23-
providerEval.variant,
24-
providerEval.reason,
25-
providerEval.errorCode,
26-
providerEval.errorMessage
22+
flagKey = flagKey,
23+
value = providerEval.value,
24+
variant = providerEval.variant,
25+
reason = providerEval.reason,
26+
errorCode = providerEval.errorCode,
27+
errorMessage = providerEval.errorMessage,
28+
metadata = providerEval.metadata
2729
)
2830
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package dev.openfeature.sdk
2+
3+
class EvaluationMetadata internal constructor(private val values: Map<String, Any>) {
4+
5+
fun getString(key: String): String? = values[key] as? String
6+
7+
fun getBoolean(key: String): Boolean? = values[key] as? Boolean
8+
9+
fun getInt(key: String): Int? = values[key] as? Int
10+
11+
fun getDouble(key: String): Double? = values[key] as? Double
12+
13+
companion object {
14+
fun builder(): Builder {
15+
return Builder()
16+
}
17+
18+
val EMPTY = EvaluationMetadata(emptyMap())
19+
}
20+
21+
override fun equals(other: Any?): Boolean {
22+
if (this === other) return true
23+
if (javaClass != other?.javaClass) return false
24+
25+
other as EvaluationMetadata
26+
27+
return values == other.values
28+
}
29+
30+
override fun hashCode(): Int {
31+
return values.hashCode()
32+
}
33+
}
34+
35+
class Builder {
36+
private val values: MutableMap<String, Any> = mutableMapOf()
37+
38+
fun putString(key: String, value: String): Builder {
39+
values[key] = value
40+
return this
41+
}
42+
43+
fun putInt(key: String, value: Int): Builder {
44+
values[key] = value
45+
return this
46+
}
47+
48+
fun putDouble(key: String, value: Double): Builder {
49+
values[key] = value
50+
return this
51+
}
52+
53+
fun putBoolean(key: String, value: Boolean): Builder {
54+
values[key] = value
55+
return this
56+
}
57+
58+
fun build(): EvaluationMetadata {
59+
return EvaluationMetadata(values.toMap())
60+
}
61+
}

android/src/main/java/dev/openfeature/sdk/ProviderEvaluation.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ data class ProviderEvaluation<T>(
77
val variant: String? = null,
88
val reason: String? = null,
99
val errorCode: ErrorCode? = null,
10-
val errorMessage: String? = null
10+
val errorMessage: String? = null,
11+
val metadata: EvaluationMetadata = EvaluationMetadata.EMPTY
1112
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package dev.openfeature.sdk
2+
3+
import org.junit.Assert
4+
import org.junit.Test
5+
6+
class EvaluationMetadataTest {
7+
8+
private val metadata = EvaluationMetadata.builder()
9+
.putString("key1", "value1")
10+
.putInt("key2", 42)
11+
.putBoolean("key3", true)
12+
.putDouble("key4", 2.71828)
13+
.build()
14+
15+
@Test
16+
fun testAddAndGet() {
17+
Assert.assertEquals("value1", metadata.getString("key1"))
18+
Assert.assertEquals(42, metadata.getInt("key2"))
19+
Assert.assertEquals(true, metadata.getBoolean("key3"))
20+
Assert.assertEquals(2.71828, metadata.getDouble("key4"))
21+
}
22+
23+
@Test
24+
fun testGetNonExistentKey() {
25+
Assert.assertNull(metadata.getString("key5"))
26+
}
27+
28+
@Test
29+
fun testInvalidType() {
30+
Assert.assertNull(metadata.getString("key2"))
31+
Assert.assertNull(metadata.getInt("key3"))
32+
Assert.assertNull(metadata.getBoolean("key4"))
33+
Assert.assertNull(metadata.getDouble("key1"))
34+
}
35+
}

android/src/test/java/dev/openfeature/sdk/FlagEvaluationsTests.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ class FlagEvaluationsTests {
7676
Assert.assertEquals(booleanDetails, client.getBooleanDetails(key, false))
7777
Assert.assertEquals(booleanDetails, client.getBooleanDetails(key, false, FlagEvaluationOptions()))
7878

79-
val stringDetails = FlagEvaluationDetails(key, "tset")
79+
// in DoSomethingProvider, the string evaluation is special since it contains some metadata values
80+
val stringDetails = FlagEvaluationDetails(key, "tset", metadata = DoSomethingProvider.evaluationMetadata)
8081
Assert.assertEquals(stringDetails, client.getStringDetails(key, "test"))
8182
Assert.assertEquals(stringDetails, client.getStringDetails(key, "test", FlagEvaluationOptions()))
8283

@@ -93,6 +94,18 @@ class FlagEvaluationsTests {
9394
Assert.assertEquals(objectDetails, client.getObjectDetails(key, Value.Structure(mapOf()), FlagEvaluationOptions()))
9495
}
9596

97+
@Test
98+
fun testMetadataFlagEvaluation() = runTest {
99+
OpenFeatureAPI.setProvider(DoSomethingProvider())
100+
val client = OpenFeatureAPI.getClient()
101+
val key = "key"
102+
103+
val details = client.getStringDetails(key, "default")
104+
val metadata: EvaluationMetadata = details.metadata
105+
Assert.assertEquals("value1", metadata.getString("key1"))
106+
Assert.assertEquals(42, metadata.getInt("key2"))
107+
}
108+
96109
@Test
97110
fun testHooksAreFired() = runTest {
98111
OpenFeatureAPI.setProvider(NoOpProvider())

android/src/test/java/dev/openfeature/sdk/helpers/DoSomethingProvider.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dev.openfeature.sdk.helpers
22

33
import dev.openfeature.sdk.EvaluationContext
4+
import dev.openfeature.sdk.EvaluationMetadata
45
import dev.openfeature.sdk.FeatureProvider
56
import dev.openfeature.sdk.Hook
67
import dev.openfeature.sdk.ProviderEvaluation
@@ -19,6 +20,12 @@ class DoSomethingProvider(
1920
override val metadata: ProviderMetadata = DoSomethingProviderMetadata(),
2021
private var dispatcher: CoroutineDispatcher = Dispatchers.IO
2122
) : FeatureProvider {
23+
companion object {
24+
val evaluationMetadata = EvaluationMetadata.builder()
25+
.putString("key1", "value1")
26+
.putInt("key2", 42)
27+
.build()
28+
}
2229
private var eventHandler = EventHandler(dispatcher)
2330

2431
override fun initialize(initialContext: EvaluationContext?) {
@@ -51,7 +58,10 @@ class DoSomethingProvider(
5158
defaultValue: String,
5259
context: EvaluationContext?
5360
): ProviderEvaluation<String> {
54-
return ProviderEvaluation(defaultValue.reversed())
61+
return ProviderEvaluation(
62+
value = defaultValue.reversed(),
63+
metadata = evaluationMetadata
64+
)
5565
}
5666

5767
override fun getIntegerEvaluation(

0 commit comments

Comments
 (0)