From 8fac731a8bf98b8cb1c6e8719f9f8b38c47d9828 Mon Sep 17 00:00:00 2001 From: vahid torkaman Date: Tue, 19 Mar 2024 09:47:29 +0100 Subject: [PATCH] contexual api and refactoring --- .../com/example/confidencedemoapp/MainVm.kt | 4 +- .../java/com/spotify/confidence/Confidence.kt | 147 ++++++++++++++++++ .../spotify/confidence/ConfidenceContext.kt | 44 ++++++ .../confidence/ConfidenceExtensions.kt | 112 +------------ .../confidence/ConfidenceFeatureProvider.kt | 20 +-- .../com/spotify/confidence/ConfidenceValue.kt | 9 ++ .../com/spotify/confidence/EventSender.kt | 57 +------ .../spotify/confidence/EventSenderEngine.kt | 2 +- .../spotify/confidence/EventSenderUploader.kt | 3 +- .../spotify/confidence/RemoteFlagResolver.kt | 107 +++++++++++++ .../client/ConfidenceRemoteClient.kt | 9 +- .../com/spotify/confidence/client/Types.kt | 9 +- .../confidence/EventSenderIntegrationTest.kt | 6 +- 13 files changed, 331 insertions(+), 198 deletions(-) create mode 100644 Provider/src/main/java/com/spotify/confidence/Confidence.kt create mode 100644 Provider/src/main/java/com/spotify/confidence/ConfidenceContext.kt create mode 100644 Provider/src/main/java/com/spotify/confidence/ConfidenceValue.kt create mode 100644 Provider/src/main/java/com/spotify/confidence/RemoteFlagResolver.kt diff --git a/ConfidenceDemoApp/src/main/java/com/example/confidencedemoapp/MainVm.kt b/ConfidenceDemoApp/src/main/java/com/example/confidencedemoapp/MainVm.kt index 17a172dc..f19a7f92 100644 --- a/ConfidenceDemoApp/src/main/java/com/example/confidencedemoapp/MainVm.kt +++ b/ConfidenceDemoApp/src/main/java/com/example/confidencedemoapp/MainVm.kt @@ -75,7 +75,7 @@ class MainVm(app: Application) : AndroidViewModel(app) { OpenFeatureAPI.setEvaluationContext(ctx) } - eventSender.emit("eventDefinitions/navigate") + eventSender.send("eventDefinitions/navigate") Log.d(TAG, "client secret is $clientSecret") Log.d(TAG, "init took ${System.currentTimeMillis() - start} ms") @@ -96,7 +96,7 @@ class MainVm(app: Application) : AndroidViewModel(app) { }.toComposeColor() _message.postValue(messageValue) _color.postValue(colorFlag) - eventSender.emit("eventDefinitions/navigate", mapOf("screen" to ConfidenceValue.String("Hello"))) + eventSender.send("eventDefinitions/navigate", mapOf("screen" to ConfidenceValue.String("Hello"))) } fun updateContext() { diff --git a/Provider/src/main/java/com/spotify/confidence/Confidence.kt b/Provider/src/main/java/com/spotify/confidence/Confidence.kt new file mode 100644 index 00000000..be9a0e6f --- /dev/null +++ b/Provider/src/main/java/com/spotify/confidence/Confidence.kt @@ -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 getValue(flag: String, defaultValue: T): T +} + +data class Evaluation( + val reason: String, + val value: T, +) + +interface FlagResolution { + val context: Map + fun getEvaluation(flag: String, defaultValue: T): Evaluation + fun 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() + private val coroutineScope = CoroutineScope(dispatcher) + private var contextMap: MutableMap = mutableMapOf() + internal val flagResolver by lazy { + RemoteFlagResolver( + clientSecret, + region, + OkHttpClient(), + dispatcher, + SdkMetadata(SDK_ID, BuildConfig.SDK_VERSION) + ) + } + + internal suspend fun resolveFlags(flags: List): 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) { + contextMap = context.toMutableMap() + } + + override fun removeContext(key: String) { + removedKeys.add(key) + contextMap.remove(key) + } + + override fun getContext(): Map = + this.root.getContext().filterKeys { removedKeys.contains(it) } + contextMap + + override suspend fun 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) -> 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 { + 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 + } +} \ No newline at end of file diff --git a/Provider/src/main/java/com/spotify/confidence/ConfidenceContext.kt b/Provider/src/main/java/com/spotify/confidence/ConfidenceContext.kt new file mode 100644 index 00000000..bfacccc7 --- /dev/null +++ b/Provider/src/main/java/com/spotify/confidence/ConfidenceContext.kt @@ -0,0 +1,44 @@ +package com.spotify.confidence + +import dev.openfeature.sdk.EvaluationContext + +interface ConfidenceContextProvider { + fun getContext(): Map +} + +typealias ConfidenceFieldsType = Map + +interface Contextual : ConfidenceContextProvider { + fun withContext(context: ConfidenceContext): Contextual + + fun putContext(context: ConfidenceContext) + fun setContext(context: Map) + 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 = 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())) + ) +} \ No newline at end of file diff --git a/Provider/src/main/java/com/spotify/confidence/ConfidenceExtensions.kt b/Provider/src/main/java/com/spotify/confidence/ConfidenceExtensions.kt index e13e980f..507a315e 100644 --- a/Provider/src/main/java/com/spotify/confidence/ConfidenceExtensions.kt +++ b/Provider/src/main/java/com/spotify/confidence/ConfidenceExtensions.kt @@ -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 - -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): 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) : ConfidenceValue() +internal suspend fun Confidence.resolveFlags(flags: List): ResolveResponse { + return flagResolver.resolve(flags) } \ No newline at end of file diff --git a/Provider/src/main/java/com/spotify/confidence/ConfidenceFeatureProvider.kt b/Provider/src/main/java/com/spotify/confidence/ConfidenceFeatureProvider.kt index 0b9ea3dc..cf6d30c5 100644 --- a/Provider/src/main/java/com/spotify/confidence/ConfidenceFeatureProvider.kt +++ b/Provider/src/main/java/com/spotify/confidence/ConfidenceFeatureProvider.kt @@ -9,10 +9,7 @@ import com.spotify.confidence.cache.ProviderCache import com.spotify.confidence.cache.ProviderCache.CacheResolveResult import com.spotify.confidence.cache.StorageFileCache import com.spotify.confidence.client.ConfidenceClient -import com.spotify.confidence.client.ConfidenceRegion -import com.spotify.confidence.client.ConfidenceRemoteClient import com.spotify.confidence.client.ResolveResponse -import com.spotify.confidence.client.SdkMetadata import dev.openfeature.sdk.EvaluationContext import dev.openfeature.sdk.FeatureProvider import dev.openfeature.sdk.Hook @@ -83,7 +80,7 @@ class ConfidenceFeatureProvider private constructor( coroutineScope.launch(networkExceptionHandler) { confidenceAPI.putContext(initialContext.toConfidenceContext()) - val resolveResponse = confidenceAPI.resolve(listOf()) + val resolveResponse = confidenceAPI.resolveFlags(listOf()) if (resolveResponse is ResolveResponse.Resolved) { val (flags, resolveToken) = resolveResponse.flags @@ -252,9 +249,8 @@ class ConfidenceFeatureProvider private constructor( @Suppress("LongParameterList") fun create( + confidence: Confidence, context: Context, - clientSecret: String, - region: ConfidenceRegion = ConfidenceRegion.GLOBAL, initialisationStrategy: InitialisationStrategy = InitialisationStrategy.FetchAndActivate, hooks: List> = listOf(), client: ConfidenceClient? = null, @@ -263,19 +259,11 @@ class ConfidenceFeatureProvider private constructor( storage: DiskStorage? = null, flagApplier: FlagApplier? = null, eventHandler: EventHandler = EventHandler(Dispatchers.IO), - dispatcher: CoroutineDispatcher = Dispatchers.IO, - confidenceAPI: Confidence? = null + dispatcher: CoroutineDispatcher = Dispatchers.IO ): ConfidenceFeatureProvider { - val confidence = confidenceAPI ?: Confidence(clientSecret) - val configuredClient = client ?: ConfidenceRemoteClient( - clientSecret = clientSecret, - sdkMetadata = SdkMetadata(SDK_ID, BuildConfig.SDK_VERSION), - region = region, - dispatcher = dispatcher - ) val diskStorage = storage ?: StorageFileCache.create(context) val flagApplierWithRetries = flagApplier ?: FlagApplierWithRetries( - client = configuredClient, + client = TODO(), dispatcher = dispatcher, diskStorage = diskStorage ) diff --git a/Provider/src/main/java/com/spotify/confidence/ConfidenceValue.kt b/Provider/src/main/java/com/spotify/confidence/ConfidenceValue.kt new file mode 100644 index 00000000..b58a0ac9 --- /dev/null +++ b/Provider/src/main/java/com/spotify/confidence/ConfidenceValue.kt @@ -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) : ConfidenceValue() +} \ No newline at end of file diff --git a/Provider/src/main/java/com/spotify/confidence/EventSender.kt b/Provider/src/main/java/com/spotify/confidence/EventSender.kt index a8279a58..f7836773 100644 --- a/Provider/src/main/java/com/spotify/confidence/EventSender.kt +++ b/Provider/src/main/java/com/spotify/confidence/EventSender.kt @@ -1,15 +1,9 @@ package com.spotify.confidence -import android.content.Context -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.channels.consumeEach -import kotlinx.coroutines.launch import java.io.File -interface EventSender { - fun emit( +interface EventSender : Contextual { + fun send( definition: String, payload: ConfidenceFieldsType = mapOf() ) @@ -21,51 +15,4 @@ interface FlushPolicy { fun reset() fun hit(event: Event) fun shouldFlush(): Boolean -} - -class EventSenderImpl internal constructor( - private val eventSenderEngine: EventSenderEngine, - private val confidenceContext: ConfidenceContextProvider, - dispatcher: CoroutineDispatcher -) : EventSender { - private val coroutineScope = CoroutineScope(dispatcher) - override fun emit( - definition: String, - payload: ConfidenceFieldsType - ) { - eventSenderEngine.emit(definition, payload, confidenceContext.confidenceContext()) - } - - override fun onLowMemory(body: (List) -> Unit): EventSender { - coroutineScope.launch { - eventSenderEngine - .onLowMemoryChannel() - .consumeEach { - body(it) - } - } - return this - } - - override fun stop() { - eventSenderEngine.stop() - } - - companion object { - fun create( - context: Context, - clientSecret: String, - flushPolicies: List = listOf(), - confidenceContext: ConfidenceContextProvider, - dispatcher: CoroutineDispatcher = Dispatchers.IO - ): EventSender { - val engine = EventSenderEngine.instance( - context, - clientSecret, - flushPolicies = flushPolicies, - dispatcher = dispatcher - ) - return EventSenderImpl(engine, confidenceContext, dispatcher) - } - } } \ No newline at end of file diff --git a/Provider/src/main/java/com/spotify/confidence/EventSenderEngine.kt b/Provider/src/main/java/com/spotify/confidence/EventSenderEngine.kt index ead1a712..4f3bdab3 100644 --- a/Provider/src/main/java/com/spotify/confidence/EventSenderEngine.kt +++ b/Provider/src/main/java/com/spotify/confidence/EventSenderEngine.kt @@ -74,7 +74,7 @@ internal class EventSenderEngine( fun onLowMemoryChannel(): Channel> { return eventStorage.onLowMemoryChannel() } - fun emit(definition: String, payload: ConfidenceFieldsType, context: ConfidenceValue.Struct) { + fun emit(definition: String, payload: ConfidenceFieldsType, context: Map) { coroutineScope.launch { val event = Event( eventDefinition = definition, diff --git a/Provider/src/main/java/com/spotify/confidence/EventSenderUploader.kt b/Provider/src/main/java/com/spotify/confidence/EventSenderUploader.kt index 47d1efd8..99ad1b9e 100644 --- a/Provider/src/main/java/com/spotify/confidence/EventSenderUploader.kt +++ b/Provider/src/main/java/com/spotify/confidence/EventSenderUploader.kt @@ -70,8 +70,7 @@ data class Event( @Contextual val eventTime: Date, val payload: Map, - @Contextual - val context: ConfidenceValue + val context: Map ) internal class EventSenderUploaderImpl( diff --git a/Provider/src/main/java/com/spotify/confidence/RemoteFlagResolver.kt b/Provider/src/main/java/com/spotify/confidence/RemoteFlagResolver.kt new file mode 100644 index 00000000..ae210310 --- /dev/null +++ b/Provider/src/main/java/com/spotify/confidence/RemoteFlagResolver.kt @@ -0,0 +1,107 @@ +package com.spotify.confidence + +import com.spotify.confidence.client.ConfidenceRegion +import com.spotify.confidence.client.ResolveResponse +import com.spotify.confidence.client.Sdk +import com.spotify.confidence.client.SdkMetadata +import com.spotify.confidence.client.await +import com.spotify.confidence.client.serializers.FlagsSerializer +import com.spotify.confidence.client.serializers.StructureSerializer +import com.spotify.confidence.client.serializers.UUIDSerializer +import dev.openfeature.sdk.DateSerializer +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlinx.serialization.Contextual +import kotlinx.serialization.Serializable +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.modules.contextual +import okhttp3.Headers +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody.Companion.toRequestBody +import okhttp3.Response + +internal interface FlagResolver { + suspend fun resolve(flags: List, context: Map): FlagResolution +} + +internal class RemoteFlagResolver( + private val clientSecret: String, + private val region: ConfidenceRegion, + private val httpClient: OkHttpClient, + private val dispatcher: CoroutineDispatcher = Dispatchers.IO, + private val sdkMetadata: SdkMetadata, +): FlagResolver { + private val headers = Headers.headersOf( + "Content-Type", + "application/json", + "Accept", + "application/json" + ) + override suspend fun resolve(flags: List, context: Map): FlagResolution { + val sdk = Sdk(sdkMetadata.sdkId, sdkMetadata.sdkVersion) + val request = ResolveFlagsRequest(flags, context, clientSecret, false, sdk) + + val response = withContext(dispatcher) { + val jsonRequest = json.encodeToString(request) + val httpRequest = Request.Builder() + .url("${baseUrl()}/v1/flags:resolve") + .headers(headers) + .post(jsonRequest.toRequestBody()) + .build() + + httpClient.newCall(httpRequest).await().toResolveFlags() + } + + return object: FlagResolution { + private val response = response + override val context: Map + get() = context + + override fun getEvaluation(flag: String, defaultValue: T): Evaluation { + TODO() + } + + } + } + + private fun baseUrl() = when (region) { + ConfidenceRegion.GLOBAL -> "https://resolver.confidence.dev" + ConfidenceRegion.EUROPE -> "https://resolver.eu.confidence.dev" + ConfidenceRegion.USA -> "https://resolver.us.confidence.dev" + } + +} + +private val json = Json { + serializersModule = SerializersModule { + contextual(UUIDSerializer) + contextual(DateSerializer) + contextual(StructureSerializer) + } +} + +@Serializable +private data class ResolveFlagsRequest( + val flags: List, + val evaluationContext: Map, + val clientSecret: String, + val apply: Boolean, + val sdk: Sdk +) + +private fun Response.toResolveFlags(): ResolveResponse { + val bodyString = body!!.string() + + // building the json class responsible for serializing the object + val networkJson = Json { + serializersModule = SerializersModule { + contextual(FlagsSerializer) + ignoreUnknownKeys = true + } + } + return ResolveResponse.Resolved(networkJson.decodeFromString(bodyString)) +} \ No newline at end of file diff --git a/Provider/src/main/java/com/spotify/confidence/client/ConfidenceRemoteClient.kt b/Provider/src/main/java/com/spotify/confidence/client/ConfidenceRemoteClient.kt index 126cc4cc..1cd9082e 100644 --- a/Provider/src/main/java/com/spotify/confidence/client/ConfidenceRemoteClient.kt +++ b/Provider/src/main/java/com/spotify/confidence/client/ConfidenceRemoteClient.kt @@ -154,11 +154,4 @@ private fun Response.toResolveFlags(): ResolveResponse { } } return ResolveResponse.Resolved(networkJson.decodeFromString(bodyString)) -} - -sealed class ResolveResponse { - object NotModified : ResolveResponse() - data class Resolved(val flags: ResolveFlags) : ResolveResponse() -} - -data class SdkMetadata(val sdkId: String, val sdkVersion: String) \ No newline at end of file +} \ No newline at end of file diff --git a/Provider/src/main/java/com/spotify/confidence/client/Types.kt b/Provider/src/main/java/com/spotify/confidence/client/Types.kt index f8320efd..9d70c8b6 100644 --- a/Provider/src/main/java/com/spotify/confidence/client/Types.kt +++ b/Provider/src/main/java/com/spotify/confidence/client/Types.kt @@ -55,4 +55,11 @@ sealed interface SchemaType { object DoubleSchema : SchemaType object StringSchema : SchemaType object BoolSchema : SchemaType -} \ No newline at end of file +} + +sealed class ResolveResponse { + object NotModified : ResolveResponse() + data class Resolved(val flags: ResolveFlags) : ResolveResponse() +} + +data class SdkMetadata(val sdkId: String, val sdkVersion: String) \ No newline at end of file diff --git a/Provider/src/test/java/com/spotify/confidence/EventSenderIntegrationTest.kt b/Provider/src/test/java/com/spotify/confidence/EventSenderIntegrationTest.kt index d3108c7b..1e3454aa 100644 --- a/Provider/src/test/java/com/spotify/confidence/EventSenderIntegrationTest.kt +++ b/Provider/src/test/java/com/spotify/confidence/EventSenderIntegrationTest.kt @@ -47,7 +47,7 @@ class EventSenderIntegrationTest { val eventCount = 4 requireNotNull(eventSender) repeat(eventCount) { - eventSender.emit("eventDefinitions/navigate") + eventSender.send("eventDefinitions/navigate") } val list = mutableListOf() for (file in directory.walkFiles()) { @@ -101,7 +101,7 @@ class EventSenderIntegrationTest { val eventCount = 4 * batchSize + 2 requireNotNull(eventSender) repeat(eventCount) { - eventSender.emit("eventDefinitions/navigate") + eventSender.send("eventDefinitions/navigate") } advanceUntilIdle() runBlocking { @@ -163,7 +163,7 @@ class EventSenderIntegrationTest { val eventCount = 4 * batchSize + 2 requireNotNull(eventSender) repeat(eventCount) { - eventSender.emit("eventDefinitions/navigate") + eventSender.send("eventDefinitions/navigate") } advanceUntilIdle() Assert.assertEquals(uploadRequestCount, eventCount / batchSize)