-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
760ff6f
commit 8fac731
Showing
13 changed files
with
331 additions
and
198 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
Provider/src/main/java/com/spotify/confidence/Confidence.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
package com.spotify.confidence | ||
|
||
import android.content.Context | ||
import com.spotify.confidence.client.ConfidenceRegion | ||
import com.spotify.confidence.client.SdkMetadata | ||
import kotlinx.coroutines.CoroutineDispatcher | ||
import kotlinx.coroutines.CoroutineScope | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.channels.consumeEach | ||
import kotlinx.coroutines.launch | ||
import okhttp3.OkHttpClient | ||
import java.io.File | ||
|
||
interface FlagEvaluator: Contextual { | ||
suspend fun <T> getValue(flag: String, defaultValue: T): T | ||
} | ||
|
||
data class Evaluation<T>( | ||
val reason: String, | ||
val value: T, | ||
) | ||
|
||
interface FlagResolution { | ||
val context: Map<String, ConfidenceValue> | ||
fun <T> getEvaluation(flag: String, defaultValue: T): Evaluation<T> | ||
fun <T> getValue(flag: String, defaultValue: T): T { | ||
return getEvaluation(flag, defaultValue).value | ||
} | ||
} | ||
|
||
class Confidence private constructor( | ||
private val clientSecret: String, | ||
private val region: ConfidenceRegion = ConfidenceRegion.GLOBAL, | ||
private val dispatcher: CoroutineDispatcher, | ||
private val eventSenderEngine: EventSenderEngine, | ||
private val root: ConfidenceContextProvider | ||
) : Contextual, EventSender, FlagEvaluator { | ||
private val removedKeys = mutableListOf<String>() | ||
private val coroutineScope = CoroutineScope(dispatcher) | ||
private var contextMap: MutableMap<String, ConfidenceValue> = mutableMapOf() | ||
internal val flagResolver by lazy { | ||
RemoteFlagResolver( | ||
clientSecret, | ||
region, | ||
OkHttpClient(), | ||
dispatcher, | ||
SdkMetadata(SDK_ID, BuildConfig.SDK_VERSION) | ||
) | ||
} | ||
|
||
internal suspend fun resolveFlags(flags: List<String>): FlagResolution { | ||
return flagResolver.resolve(flags, getContext()) | ||
} | ||
|
||
override fun putContext(key: String, value: ConfidenceValue) { | ||
contextMap[key] = value | ||
} | ||
|
||
override fun putContext(context: ConfidenceContext) { | ||
putContext(context.name, context.value) | ||
} | ||
|
||
override fun setContext(context: Map<String, ConfidenceValue>) { | ||
contextMap = context.toMutableMap() | ||
} | ||
|
||
override fun removeContext(key: String) { | ||
removedKeys.add(key) | ||
contextMap.remove(key) | ||
} | ||
|
||
override fun getContext(): Map<String, ConfidenceValue> = | ||
this.root.getContext().filterKeys { removedKeys.contains(it) } + contextMap | ||
|
||
override suspend fun <T> getValue(flag: String, defaultValue: T): T { | ||
val response = resolveFlags(listOf(flag)) | ||
return response.getValue(flag, defaultValue) | ||
} | ||
|
||
override fun withContext(context: ConfidenceContext) = Confidence( | ||
clientSecret, | ||
region, | ||
dispatcher, | ||
eventSenderEngine, | ||
this | ||
).also { | ||
it.putContext(context) | ||
} | ||
override fun send( | ||
definition: String, | ||
payload: ConfidenceFieldsType | ||
) { | ||
eventSenderEngine.emit(definition, payload, getContext()) | ||
} | ||
|
||
override fun onLowMemory(body: (List<File>) -> Unit): EventSender { | ||
coroutineScope.launch { | ||
eventSenderEngine | ||
.onLowMemoryChannel() | ||
.consumeEach { | ||
body(it) | ||
} | ||
} | ||
return this | ||
} | ||
|
||
override fun stop() { | ||
eventSenderEngine.stop() | ||
} | ||
|
||
companion object { | ||
fun create( | ||
context: Context, | ||
clientSecret: String, | ||
region: ConfidenceRegion = ConfidenceRegion.GLOBAL, | ||
dispatcher: CoroutineDispatcher = Dispatchers.IO | ||
): EventSender { | ||
val engine = EventSenderEngine.instance( | ||
context, | ||
clientSecret, | ||
flushPolicies = listOf(confidenceFlushPolicy), | ||
dispatcher = dispatcher | ||
) | ||
val confidenceContext = object : ConfidenceContextProvider { | ||
override fun getContext(): Map<String, ConfidenceValue> { | ||
return emptyMap() | ||
} | ||
} | ||
return Confidence(clientSecret, region, dispatcher, engine, confidenceContext) | ||
} | ||
} | ||
} | ||
|
||
private val confidenceFlushPolicy = object : FlushPolicy { | ||
private var size = 0 | ||
override fun reset() { | ||
size = 0 | ||
} | ||
|
||
override fun hit(event: Event) { | ||
size++ | ||
} | ||
|
||
override fun shouldFlush(): Boolean { | ||
return size > 4 | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
Provider/src/main/java/com/spotify/confidence/ConfidenceContext.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.spotify.confidence | ||
|
||
import dev.openfeature.sdk.EvaluationContext | ||
|
||
interface ConfidenceContextProvider { | ||
fun getContext(): Map<String, ConfidenceValue> | ||
} | ||
|
||
typealias ConfidenceFieldsType = Map<String, ConfidenceValue> | ||
|
||
interface Contextual : ConfidenceContextProvider { | ||
fun withContext(context: ConfidenceContext): Contextual | ||
|
||
fun putContext(context: ConfidenceContext) | ||
fun setContext(context: Map<String, ConfidenceValue>) | ||
fun putContext(key: String, value: ConfidenceValue) | ||
fun removeContext(key: String) | ||
} | ||
|
||
interface ConfidenceContext { | ||
val name: String | ||
val value: ConfidenceValue | ||
} | ||
|
||
class PageContext(private val page: String) : ConfidenceContext { | ||
override val value: ConfidenceValue | ||
get() = ConfidenceValue.String(page) | ||
override val name: String | ||
get() = "page" | ||
} | ||
|
||
class CommonContext : ConfidenceContextProvider { | ||
override fun getContext(): Map<String, ConfidenceValue> = mapOf() | ||
} | ||
|
||
fun EvaluationContext.toConfidenceContext() = object : ConfidenceContext { | ||
override val name: String = "open_feature" | ||
override val value: ConfidenceValue | ||
get() = ConfidenceValue.Struct( | ||
asMap() | ||
.map { it.key to ConfidenceValue.String(it.value.toString()) } | ||
.toMap() + ("targeting_key" to ConfidenceValue.String(getTargetingKey())) | ||
) | ||
} |
112 changes: 2 additions & 110 deletions
112
Provider/src/main/java/com/spotify/confidence/ConfidenceExtensions.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,115 +1,7 @@ | ||
package com.spotify.confidence | ||
|
||
import android.content.Context | ||
import com.spotify.confidence.client.ResolveResponse | ||
import dev.openfeature.sdk.EvaluationContext | ||
import kotlinx.coroutines.CoroutineDispatcher | ||
import kotlinx.coroutines.Dispatchers | ||
|
||
interface ConfidenceContextProvider { | ||
fun confidenceContext(): ConfidenceValue.Struct | ||
} | ||
|
||
typealias ConfidenceFieldsType = Map<String, ConfidenceValue> | ||
|
||
interface ConfidenceAPI : ConfidenceContextProvider { | ||
fun fork(context: ConfidenceContext): ConfidenceAPI | ||
fun putContext(context: ConfidenceContext) | ||
} | ||
|
||
interface ConfidenceContext { | ||
val name: String | ||
val value: ConfidenceValue | ||
} | ||
|
||
class PageContext(private val page: String) : ConfidenceContext { | ||
override val value: ConfidenceValue | ||
get() = ConfidenceValue.String(page) | ||
override val name: String | ||
get() = "page" | ||
} | ||
|
||
class CommonContext : ConfidenceContextProvider { | ||
override fun confidenceContext(): ConfidenceValue.Struct = ConfidenceValue.Struct(mapOf()) | ||
} | ||
|
||
fun EvaluationContext.toConfidenceContext() = object : ConfidenceContext { | ||
override val name: String = "open_feature" | ||
override val value: ConfidenceValue | ||
get() = ConfidenceValue.Struct( | ||
asMap() | ||
.map { it.key to ConfidenceValue.String(it.value.toString()) } | ||
.toMap() + ("targeting_key" to ConfidenceValue.String(getTargetingKey())) | ||
) | ||
} | ||
|
||
class Confidence( | ||
val clientSecret: String, | ||
private val root: ConfidenceContextProvider = CommonContext() | ||
) : ConfidenceAPI { | ||
private var contextMap: ConfidenceValue.Struct = ConfidenceValue.Struct(mapOf()) | ||
override fun putContext(context: ConfidenceContext) { | ||
val map = contextMap.value.toMutableMap() | ||
map[context.name] = context.value | ||
contextMap = ConfidenceValue.Struct(map) | ||
} | ||
|
||
override fun confidenceContext(): ConfidenceValue.Struct { | ||
return ConfidenceValue.Struct(root.confidenceContext().value + contextMap.value) | ||
} | ||
|
||
override fun fork(context: ConfidenceContext) = Confidence(clientSecret, this).also { | ||
it.putContext(context) | ||
} | ||
} | ||
|
||
internal fun ConfidenceAPI.resolve(flags: List<String>): ResolveResponse { | ||
TODO() | ||
} | ||
|
||
fun Confidence.openFeatureProvider( | ||
context: Context, | ||
initialisationStrategy: InitialisationStrategy | ||
): ConfidenceFeatureProvider = ConfidenceFeatureProvider.create( | ||
context, | ||
confidenceAPI = this, | ||
clientSecret = clientSecret, | ||
initialisationStrategy = initialisationStrategy | ||
) | ||
|
||
fun Confidence.eventSender( | ||
context: Context, | ||
dispatcher: CoroutineDispatcher = Dispatchers.IO | ||
): EventSender = EventSenderImpl.create( | ||
clientSecret = clientSecret, | ||
dispatcher = dispatcher, | ||
flushPolicies = listOf(confidenceFlushPolicy), | ||
context = context, | ||
confidenceContext = this | ||
).onLowMemory { files -> | ||
val sortedFiles = files.sortedBy { it.lastModified() } | ||
sortedFiles.take(10).forEach { it.delete() } | ||
} | ||
|
||
val confidenceFlushPolicy = object : FlushPolicy { | ||
private var size = 0 | ||
override fun reset() { | ||
size = 0 | ||
} | ||
|
||
override fun hit(event: Event) { | ||
size++ | ||
} | ||
|
||
override fun shouldFlush(): Boolean { | ||
return size > 4 | ||
} | ||
} | ||
|
||
sealed class ConfidenceValue { | ||
data class String(val value: kotlin.String) : ConfidenceValue() | ||
data class Double(val value: kotlin.Double) : ConfidenceValue() | ||
data class Boolean(val value: kotlin.Boolean) : ConfidenceValue() | ||
data class Int(val value: kotlin.Int) : ConfidenceValue() | ||
data class Struct(val value: Map<kotlin.String, ConfidenceValue>) : ConfidenceValue() | ||
internal suspend fun Confidence.resolveFlags(flags: List<String>): ResolveResponse { | ||
return flagResolver.resolve(flags) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
Provider/src/main/java/com/spotify/confidence/ConfidenceValue.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.spotify.confidence | ||
|
||
sealed class ConfidenceValue { | ||
data class String(val value: kotlin.String) : ConfidenceValue() | ||
data class Double(val value: kotlin.Double) : ConfidenceValue() | ||
data class Boolean(val value: kotlin.Boolean) : ConfidenceValue() | ||
data class Int(val value: kotlin.Int) : ConfidenceValue() | ||
data class Struct(val value: Map<kotlin.String, ConfidenceValue>) : ConfidenceValue() | ||
} |
Oops, something went wrong.