Skip to content

Commit

Permalink
bring back flag apply
Browse files Browse the repository at this point in the history
  • Loading branch information
vahidlazio committed Mar 28, 2024
1 parent 443a87d commit f1919a2
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 48 deletions.
34 changes: 31 additions & 3 deletions Provider/src/main/java/com/spotify/confidence/Confidence.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.spotify.confidence

import android.content.Context
import com.spotify.confidence.apply.FlagApplierWithRetries
import com.spotify.confidence.cache.FileDiskStorage
import com.spotify.confidence.client.ConfidenceRegion
import com.spotify.confidence.client.FlagApplierClientImpl
import com.spotify.confidence.client.SdkMetadata
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
Expand All @@ -16,11 +19,13 @@ class Confidence private constructor(
private val dispatcher: CoroutineDispatcher,
private val eventSenderEngine: EventSenderEngine,
private val root: ConfidenceContextProvider,
private val region: ConfidenceRegion = ConfidenceRegion.GLOBAL
private val region: ConfidenceRegion = ConfidenceRegion.GLOBAL,
private val flagApplier: FlagApplierWithRetries
) : Contextual, EventSender {
private val removedKeys = mutableListOf<String>()
private val coroutineScope = CoroutineScope(dispatcher)
private var contextMap: MutableMap<String, ConfidenceValue> = mutableMapOf()

private val flagResolver by lazy {
RemoteFlagResolver(
clientSecret,
Expand All @@ -41,6 +46,10 @@ class Confidence private constructor(
return flagResolver.resolve(flags, context)
}

fun applyFlag(flagName: String, resolveToken: String) {
flagApplier.apply(flagName, resolveToken)
}

override fun putContext(key: String, value: ConfidenceValue) {
contextMap[key] = value
}
Expand All @@ -66,7 +75,8 @@ class Confidence private constructor(
dispatcher,
eventSenderEngine,
this,
region
region,
flagApplier
).also {
it.putContext(context)
}
Expand Down Expand Up @@ -110,7 +120,25 @@ class Confidence private constructor(
return emptyMap()
}
}
return Confidence(clientSecret, dispatcher, engine, confidenceContext, region)
val flagApplierClient = FlagApplierClientImpl(
clientSecret,
SdkMetadata(SDK_ID, BuildConfig.SDK_VERSION),
region,
dispatcher
)
val flagApplier = FlagApplierWithRetries(
client = flagApplierClient,
dispatcher = dispatcher,
diskStorage = FileDiskStorage.create(context)
)
return Confidence(
clientSecret,
dispatcher,
engine,
confidenceContext,
region,
flagApplier
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ class ConfidenceFeatureProvider private constructor(
key,
defaultValue,
context.toConfidenceContext().map
).toProviderEvaluation()
) { flagName, resolveToken ->
confidenceAPI.applyFlag(flagName, resolveToken)
}.toProviderEvaluation()
}
companion object {
private class ConfidenceMetadata(override var name: String? = "confidence") : ProviderMetadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import com.spotify.confidence.client.ResolveReason
internal fun <T> FlagResolution.getEvaluation(
flag: String,
defaultValue: T,
context: Map<String, ConfidenceValue>
context: Map<String, ConfidenceValue>,
applyFlag: (String, String) -> Unit = { _, _ -> }
): Evaluation<T> {
val parsedKey = FlagKey(flag)
val resolvedFlag = this.flags.firstOrNull { it.flag == parsedKey.flagName }
Expand All @@ -15,6 +16,10 @@ internal fun <T> FlagResolution.getEvaluation(
errorCode = ErrorCode.FLAG_NOT_FOUND
)

if (resolvedFlag.reason != ResolveReason.RESOLVE_REASON_TARGETING_KEY_ERROR) {
applyFlag(parsedKey.flagName, resolveToken)
}

// handle stale case
if (this.context != context) {
return Evaluation(
Expand Down Expand Up @@ -96,14 +101,6 @@ private data class FlagKey(
}
}

internal fun <T> FlagResolution.getValue(
flag: String,
defaultValue: T,
context: Map<String, ConfidenceValue>
): T {
return getEvaluation(flag, defaultValue, context).value
}

enum class ErrorCode {
// The value was resolved before the provider was ready.
RESOLVE_STALE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.spotify.confidence.apply
import com.spotify.confidence.EventProcessor
import com.spotify.confidence.cache.DiskStorage
import com.spotify.confidence.client.AppliedFlag
import com.spotify.confidence.client.ConfidenceClient
import com.spotify.confidence.client.FlagApplierClient
import com.spotify.confidence.client.Result
import com.spotify.confidence.client.serializers.DateSerializer
import kotlinx.coroutines.CoroutineDispatcher
Expand Down Expand Up @@ -43,7 +43,7 @@ internal typealias FlagsAppliedMap =
MutableMap<String, MutableMap<String, ApplyInstance>>

class FlagApplierWithRetries(
private val client: ConfidenceClient,
private val client: FlagApplierClient,
private val dispatcher: CoroutineDispatcher,
private val diskStorage: DiskStorage
) : FlagApplier {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.spotify.confidence.client

interface ConfidenceClient {
interface FlagApplierClient {
suspend fun apply(flags: List<AppliedFlag>, resolveToken: String): Result
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ import com.spotify.confidence.client.network.ApplyFlagsInteractorImpl
import com.spotify.confidence.client.network.ApplyFlagsRequest
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import okhttp3.Headers
import okhttp3.HttpUrl
import okhttp3.OkHttpClient
import okhttp3.Response

internal class ConfidenceRemoteClient : ConfidenceClient {
internal class FlagApplierClientImpl : FlagApplierClient {
private val clientSecret: String
private val sdkMetadata: SdkMetadata
private val okHttpClient: OkHttpClient
Expand Down Expand Up @@ -99,16 +96,4 @@ internal class ConfidenceRemoteClient : ConfidenceClient {

return result
}
}

private fun Response.toResolveFlags(): ResolveResponse {
val bodyString = body!!.string()

// building the json class responsible for serializing the object
val networkJson = Json {
serializersModule = SerializersModule {
ignoreUnknownKeys = true
}
}
return ResolveResponse.Resolved(networkJson.decodeFromString(bodyString))
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import com.spotify.confidence.cache.APPLY_FILE_NAME
import com.spotify.confidence.cache.json
import com.spotify.confidence.cache.toCacheData
import com.spotify.confidence.client.AppliedFlag
import com.spotify.confidence.client.ConfidenceClient
import com.spotify.confidence.client.FlagApplierClient
import com.spotify.confidence.client.Flags
import com.spotify.confidence.client.ResolveFlags
import com.spotify.confidence.client.ResolveReason
Expand Down Expand Up @@ -81,7 +81,7 @@ private const val cacheFileData = "{\n" +

@OptIn(ExperimentalCoroutinesApi::class)
internal class ConfidenceFeatureProviderTests {
private val mockClient: ConfidenceClient = mock()
private val mockClient: FlagApplierClient = mock()
private val mockContext: Context = mock()
private val instant = Instant.parse("2023-03-01T14:01:46.645Z")
private val blueStringValues = mutableMapOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ package com.spotify.confidence

import com.spotify.confidence.client.AppliedFlag
import com.spotify.confidence.client.Clock
import com.spotify.confidence.client.ConfidenceRemoteClient
import com.spotify.confidence.client.FlagApplierClientImpl
import com.spotify.confidence.client.Flags
import com.spotify.confidence.client.ResolveFlags
import com.spotify.confidence.client.ResolveReason
Expand Down Expand Up @@ -109,7 +109,7 @@ internal class ConfidenceRemoteClientTests {
.setResponseCode(200)
.setBody(jsonPayload)
)
val parsedResponse = ConfidenceRemoteClient(
val parsedResponse = FlagApplierClientImpl(
baseUrl = mockWebServer.url("/v1/flags:resolve"),
dispatcher = testDispatcher
)
Expand Down Expand Up @@ -167,7 +167,7 @@ internal class ConfidenceRemoteClientTests {
.setBody(jsonPayload)
)
val parsedResponse =
ConfidenceRemoteClient(
FlagApplierClientImpl(
baseUrl = mockWebServer.url("/v1/flags:resolve"),
dispatcher = testDispatcher
)
Expand Down Expand Up @@ -217,7 +217,7 @@ internal class ConfidenceRemoteClientTests {
.setBody(jsonPayload)
)
val parsedResponse =
ConfidenceRemoteClient(
FlagApplierClientImpl(
baseUrl = mockWebServer.url("/v1/flags:resolve"),
dispatcher = testDispatcher
)
Expand Down Expand Up @@ -273,7 +273,7 @@ internal class ConfidenceRemoteClientTests {
val ex = assertThrows(ParseError::class.java) {
runTest {
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
ConfidenceRemoteClient(
FlagApplierClientImpl(
baseUrl = mockWebServer.url("/v1/flags:resolve"),
dispatcher = testDispatcher
)
Expand Down Expand Up @@ -314,7 +314,7 @@ internal class ConfidenceRemoteClientTests {
val ex = assertThrows(ParseError::class.java) {
runTest {
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
ConfidenceRemoteClient(
FlagApplierClientImpl(
baseUrl = mockWebServer.url("/v1/flags:resolve"),
dispatcher = testDispatcher
)
Expand Down Expand Up @@ -352,7 +352,7 @@ internal class ConfidenceRemoteClientTests {
val ex = assertThrows(ParseError::class.java) {
runTest {
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
ConfidenceRemoteClient(
FlagApplierClientImpl(
baseUrl = mockWebServer.url("/v1/flags:resolve"),
dispatcher = testDispatcher
)
Expand Down Expand Up @@ -391,7 +391,7 @@ internal class ConfidenceRemoteClientTests {
val ex = assertThrows(ParseError::class.java) {
runTest {
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
ConfidenceRemoteClient(
FlagApplierClientImpl(
baseUrl = mockWebServer.url("/v1/flags:resolve"),
dispatcher = testDispatcher
)
Expand Down Expand Up @@ -450,7 +450,7 @@ internal class ConfidenceRemoteClientTests {
}
}

ConfidenceRemoteClient(
FlagApplierClientImpl(
"secret1",
sdkMetadata,
mockWebServer.url("/v1/flags:resolve"),
Expand Down Expand Up @@ -512,7 +512,7 @@ internal class ConfidenceRemoteClientTests {
return MockResponse().setResponseCode(200)
}
}
ConfidenceRemoteClient(
FlagApplierClientImpl(
"secret1",
sdkMetadata,
mockWebServer.url("/v1/flags:apply"),
Expand All @@ -535,7 +535,7 @@ internal class ConfidenceRemoteClientTests {
return MockResponse().setResponseCode(200)
}
}
val result = ConfidenceRemoteClient(
val result = FlagApplierClientImpl(
"secret1",
sdkMetadata,
mockWebServer.url("/v1/flags:apply"),
Expand All @@ -560,7 +560,7 @@ internal class ConfidenceRemoteClientTests {
return MockResponse().setResponseCode(500)
}
}
val result = ConfidenceRemoteClient(
val result = FlagApplierClientImpl(
"secret1",
sdkMetadata,
mockWebServer.url("/v1/flags:apply"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
package com.spotify.confidence

import android.content.Context
import com.spotify.confidence.client.ConfidenceClient
import com.spotify.confidence.client.FlagApplierClient
import com.spotify.confidence.client.Flags
import com.spotify.confidence.client.ResolveFlags
import com.spotify.confidence.client.ResolveReason
Expand Down Expand Up @@ -64,7 +64,7 @@ class StorageFileCacheTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testOfflineScenarioLoadsStoredCache() = runTest {
val mockClient: ConfidenceClient = mock()
val mockClient: FlagApplierClient = mock()
whenever(mockClient.apply(any(), any())).thenReturn(Result.Success)
val testDispatcher = UnconfinedTestDispatcher(testScheduler)
val cache1 = InMemoryCache()
Expand Down

0 comments on commit f1919a2

Please sign in to comment.