Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global ctx passed into the provider via FeatureProvider #9

Merged
merged 1 commit into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,7 @@ class ConfidenceFeatureProvider private constructor(
)
}

private var currEvaluationContext: EvaluationContext? = null

override suspend fun initialize(initialContext: EvaluationContext?) {
currEvaluationContext = initialContext
Copy link
Member Author

@fabriziodemaria fabriziodemaria May 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initialContext is still passed via inizitialize for the Provider to be able to warm up the cache. Subsequent evaluations will use the global context defined in the SDK, and if the cached-context is different than the evaluation-context, the evaluation will fail with reason STALE

if (initialContext == null) return
try {
val (resolvedFlags, resolveToken) = client.resolve(listOf(), initialContext)
Expand All @@ -87,42 +84,47 @@ class ConfidenceFeatureProvider private constructor(

override fun getBooleanEvaluation(
key: String,
defaultValue: Boolean
defaultValue: Boolean,
context: EvaluationContext?
): ProviderEvaluation<Boolean> {
return generateEvaluation(key, defaultValue)
return generateEvaluation(key, defaultValue, context)
}

override fun getDoubleEvaluation(
key: String,
defaultValue: Double
defaultValue: Double,
context: EvaluationContext?
): ProviderEvaluation<Double> {
return generateEvaluation(key, defaultValue)
return generateEvaluation(key, defaultValue, context)
}

override fun getIntegerEvaluation(
key: String,
defaultValue: Int
defaultValue: Int,
context: EvaluationContext?
): ProviderEvaluation<Int> {
return generateEvaluation(key, defaultValue)
return generateEvaluation(key, defaultValue, context)
}

override fun getObjectEvaluation(
key: String,
defaultValue: Value
defaultValue: Value,
context: EvaluationContext?
): ProviderEvaluation<Value> {
return generateEvaluation(key, defaultValue)
return generateEvaluation(key, defaultValue, context)
}

override fun getStringEvaluation(
key: String,
defaultValue: String
defaultValue: String,
context: EvaluationContext?
): ProviderEvaluation<String> {
return generateEvaluation(key, defaultValue)
return generateEvaluation(key, defaultValue, context)
}

private fun <T> generateEvaluation(key: String, defaultValue: T): ProviderEvaluation<T> {
private fun <T> generateEvaluation(key: String, defaultValue: T, context: EvaluationContext?): ProviderEvaluation<T> {
val parsedKey = FlagKey(key)
val evaluationContext = currEvaluationContext ?: throw ProviderNotReadyError()
val evaluationContext = context ?: throw InvalidContextError()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A missing context here means that no global context has been set in the global SDK, but a context is required in our Provider. It's not the Provider that is not ready, but the Context from the SDK that is invalid/missing.

return when(val resolve: CacheResolveResult = cache.resolve(parsedKey.flagName, evaluationContext)) {
is CacheResolveResult.Found -> {
val resolvedFlag = resolve.entry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ internal class ConfidenceFeatureProviderTests {
confidenceFeatureProvider.initialize(MutableContext("foo"))
}
verify(mockClient, times(1)).resolve(any(), eq(MutableContext("foo")))
val evalString = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "default")
val evalBool = confidenceFeatureProvider.getBooleanEvaluation("fdema-kotlin-flag-1.myboolean", true)
val evalInteger = confidenceFeatureProvider.getIntegerEvaluation("fdema-kotlin-flag-1.myinteger", 1)
val evalDouble = confidenceFeatureProvider.getDoubleEvaluation("fdema-kotlin-flag-1.mydouble", 7.28)
val evalDate = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mydate", "error")
val evalObject = confidenceFeatureProvider.getObjectEvaluation("fdema-kotlin-flag-1.mystruct", Value.Structure(mapOf()))
val evalNested = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystruct.innerString", "error")
val evalNull = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mynull", "error")
val evalString = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "default", MutableContext("foo"))
val evalBool = confidenceFeatureProvider.getBooleanEvaluation("fdema-kotlin-flag-1.myboolean", true, MutableContext("foo"))
val evalInteger = confidenceFeatureProvider.getIntegerEvaluation("fdema-kotlin-flag-1.myinteger", 1, MutableContext("foo"))
val evalDouble = confidenceFeatureProvider.getDoubleEvaluation("fdema-kotlin-flag-1.mydouble", 7.28, MutableContext("foo"))
val evalDate = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mydate", "error", MutableContext("foo"))
val evalObject = confidenceFeatureProvider.getObjectEvaluation("fdema-kotlin-flag-1.mystruct", Value.Structure(mapOf()), MutableContext("foo"))
val evalNested = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystruct.innerString", "error", MutableContext("foo"))
val evalNull = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mynull", "error", MutableContext("foo"))

advanceUntilIdle()
verify(mockClient, times(8)).apply(any(), eq("token1"))
Expand Down Expand Up @@ -154,7 +154,7 @@ internal class ConfidenceFeatureProviderTests {
runBlocking {
confidenceFeatureProvider.initialize(MutableContext("foo"))
}
val evalRootObject = confidenceFeatureProvider.getObjectEvaluation("fdema-kotlin-flag-1", Value.Structure(mapOf()))
val evalRootObject = confidenceFeatureProvider.getObjectEvaluation("fdema-kotlin-flag-1", Value.Structure(mapOf()), MutableContext("foo"))

assertEquals(resolvedValueAsMap, evalRootObject.value.asStructure())
assertEquals(Reason.TARGETING_MATCH.toString(), evalRootObject.reason)
Expand All @@ -172,20 +172,17 @@ internal class ConfidenceFeatureProviderTests {
.build()

whenever(mockClient.resolve(eq(listOf()), any())).thenReturn(ResolveFlagsResponse(resolvedFlags, "token1"))
runBlocking {
confidenceFeatureProvider.initialize(MutableContext("user2"))
}

// Simulate a case where the context in the cache is not synced with the evaluation's context
cache.refresh(resolvedFlags, "token2", MutableContext("user1"))

val evalString = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "default")
val evalBool = confidenceFeatureProvider.getBooleanEvaluation("fdema-kotlin-flag-1.myboolean", true)
val evalInteger = confidenceFeatureProvider.getIntegerEvaluation("fdema-kotlin-flag-1.myinteger", 1)
val evalDouble = confidenceFeatureProvider.getDoubleEvaluation("fdema-kotlin-flag-1.mydouble", 7.28)
val evalDate = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mydate", "default1")
val evalObject = confidenceFeatureProvider.getObjectEvaluation("fdema-kotlin-flag-1.mystruct", Value.Structure(mapOf()))
val evalNested = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystruct.innerString", "default2")
val evalNull = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mynull", "default3")
val evalString = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "default", MutableContext("user2"))
val evalBool = confidenceFeatureProvider.getBooleanEvaluation("fdema-kotlin-flag-1.myboolean", true, MutableContext("user2"))
val evalInteger = confidenceFeatureProvider.getIntegerEvaluation("fdema-kotlin-flag-1.myinteger", 1, MutableContext("user2"))
val evalDouble = confidenceFeatureProvider.getDoubleEvaluation("fdema-kotlin-flag-1.mydouble", 7.28, MutableContext("user2"))
val evalDate = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mydate", "default1", MutableContext("user2"))
val evalObject = confidenceFeatureProvider.getObjectEvaluation("fdema-kotlin-flag-1.mystruct", Value.Structure(mapOf()), MutableContext("user2"))
val evalNested = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystruct.innerString", "default2", MutableContext("user2"))
val evalNull = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mynull", "default3", MutableContext("user2"))

assertEquals("default", evalString.value)
assertEquals(true, evalBool.value)
Expand Down Expand Up @@ -255,7 +252,7 @@ internal class ConfidenceFeatureProviderTests {
confidenceFeatureProvider.initialize(MutableContext("user1"))
}

val evalString = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "default")
val evalString = confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-1.mystring", "default", MutableContext("user1"))

assertNull(evalString.errorMessage)
assertNull(evalString.errorCode)
Expand All @@ -273,15 +270,11 @@ internal class ConfidenceFeatureProviderTests {
.build()

whenever(mockClient.resolve(eq(listOf()), any())).thenReturn(ResolveFlagsResponse(resolvedFlags, "token1"))
runBlocking {
confidenceFeatureProvider.initialize(MutableContext("user2"))
}
// Simulate a case where the context in the cache is not synced with the evaluation's context
// This shouldn't have an effect in this test, given that not found values are priority over stale values
cache.refresh(resolvedFlags, "token2", MutableContext("user1"))
// TODO Should flagNotFound throw?
val ex = assertThrows(FlagNotFoundError::class.java) {
confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-2.mystring", "default")
confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-2.mystring", "default", MutableContext("user2"))
}
assertEquals("Could not find flag named: fdema-kotlin-flag-2", ex.message)
}
Expand All @@ -298,9 +291,8 @@ internal class ConfidenceFeatureProviderTests {
runBlocking {
confidenceFeatureProvider.initialize(MutableContext("user1"))
}
// TODO Should flagNotFound throw?
val ex = assertThrows(FlagNotFoundError::class.java) {
confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-2.mystring", "default")
confidenceFeatureProvider.getStringEvaluation("fdema-kotlin-flag-2.mystring", "default", MutableContext("user1"))
}
assertEquals("Could not find flag named: fdema-kotlin-flag-2", ex.message)
}
Expand All @@ -319,7 +311,8 @@ internal class ConfidenceFeatureProviderTests {
val ex = assertThrows(ParseError::class.java) {
confidenceFeatureProvider.getStringEvaluation(
"fdema-kotlin-flag-1.wrongid",
"default"
"default",
MutableContext("user2")
)
}
assertEquals("Unable to parse flag value: wrongid", ex.message)
Expand All @@ -339,7 +332,8 @@ internal class ConfidenceFeatureProviderTests {
val ex = assertThrows(ParseError::class.java) {
confidenceFeatureProvider.getStringEvaluation(
"fdema-kotlin-flag-1.mystring.extrapath",
"default"
"default",
MutableContext("user2")
)
}
assertEquals("Unable to parse flag value: mystring/extrapath", ex.message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,14 @@ class StorageFileCacheTests {
.client(mockClient)
.cache(cache2)
.build()
runBlocking {
provider2.initialize(MutableContext(targetingKey = "user1"))
}
val evalString = provider2.getStringEvaluation("fdema-kotlin-flag-1.mystring", "default")
val evalBool = provider2.getBooleanEvaluation("fdema-kotlin-flag-1.myboolean", true)
val evalInteger = provider2.getIntegerEvaluation("fdema-kotlin-flag-1.myinteger", 1)
val evalDouble = provider2.getDoubleEvaluation("fdema-kotlin-flag-1.mydouble", 7.28)
val evalDate = provider2.getStringEvaluation("fdema-kotlin-flag-1.mydate", "error")
val evalObject = provider2.getObjectEvaluation("fdema-kotlin-flag-1.mystruct", Value.Structure(mapOf()))
val evalNested = provider2.getStringEvaluation("fdema-kotlin-flag-1.mystruct.innerString", "error")
val evalNull = provider2.getStringEvaluation("fdema-kotlin-flag-1.mynull", "error")
val evalString = provider2.getStringEvaluation("fdema-kotlin-flag-1.mystring", "default", MutableContext("user1"))
val evalBool = provider2.getBooleanEvaluation("fdema-kotlin-flag-1.myboolean", true, MutableContext("user1"))
val evalInteger = provider2.getIntegerEvaluation("fdema-kotlin-flag-1.myinteger", 1, MutableContext("user1"))
val evalDouble = provider2.getDoubleEvaluation("fdema-kotlin-flag-1.mydouble", 7.28, MutableContext("user1"))
val evalDate = provider2.getStringEvaluation("fdema-kotlin-flag-1.mydate", "error", MutableContext("user1"))
val evalObject = provider2.getObjectEvaluation("fdema-kotlin-flag-1.mystruct", Value.Structure(mapOf()), MutableContext("user1"))
val evalNested = provider2.getStringEvaluation("fdema-kotlin-flag-1.mystruct.innerString", "error", MutableContext("user1"))
val evalNull = provider2.getStringEvaluation("fdema-kotlin-flag-1.mynull", "error", MutableContext("user1"))

TestCase.assertEquals("red", evalString.value)
TestCase.assertEquals(false, evalBool.value)
Expand Down