Skip to content
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
15 changes: 15 additions & 0 deletions Confidence/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
id("signing")
id("org.jetbrains.kotlinx.binary-compatibility-validator")
id("org.jetbrains.kotlinx.kover")
alias(libs.plugins.protobuf)
}

val providerVersion = project.extra["version"].toString()
Expand Down Expand Up @@ -60,6 +61,20 @@ dependencies {
testImplementation(libs.mockWebServer)
testImplementation(libs.mockk)
testImplementation(libs.kotlinxCoroutinesTest)
testImplementation(libs.protobufJava)
}

protobuf {
protoc {
artifact = libs.protoc.get().toString()
}
generateProtoTasks {
all().configureEach {
builtins {
create("java")
}
}
}
}

publishing {
Expand Down
25 changes: 20 additions & 5 deletions Confidence/src/main/java/com/spotify/confidence/Confidence.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class Confidence internal constructor(
private val flagApplierClient: FlagApplierClient,
private val parent: ConfidenceContextProvider? = null,
private val region: ConfidenceRegion = ConfidenceRegion.GLOBAL,
private val debugLogger: DebugLogger?
private val debugLogger: DebugLogger?,
internal val telemetry: Telemetry = Telemetry(SDK_ID, Telemetry.Library.CONFIDENCE, SDK_VERSION)
) : Contextual, EventSender {
private val removedKeys = mutableListOf<String>()
private val contextMap = MutableStateFlow(initialContext)
Expand Down Expand Up @@ -123,6 +124,12 @@ class Confidence internal constructor(
apply(flagName, resolveToken)
}
}
val (telemetryReason, telemetryErrorCode) = Telemetry.mapEvaluationReason(
eval.reason,
eval.errorCode
)
telemetry.trackEvaluation(telemetryReason, telemetryErrorCode)

// we are using a custom serializer so that the Json is serialized correctly in the logs
val contextJson = Json.encodeToJsonElement(
MapSerializer(String.serializer(), NetworkConfidenceValueSerializer),
Expand Down Expand Up @@ -219,6 +226,11 @@ class Confidence internal constructor(
it.getContext().filterKeys { key -> !removedKeys.contains(key) } + contextMap.value
} ?: contextMap.value

@Suppress("unused")
private fun setTelemetryLibraryOpenFeature() {
telemetry.library = Telemetry.Library.OPEN_FEATURE
}

override fun withContext(context: Map<String, ConfidenceValue>): EventSender = Confidence(
clientSecret,
dispatcher,
Expand All @@ -230,7 +242,8 @@ class Confidence internal constructor(
flagApplierClient,
this,
region,
debugLogger
debugLogger,
telemetry
).also {
it.putContext(context)
}
Expand Down Expand Up @@ -377,6 +390,7 @@ object ConfidenceFactory {
DebugLoggerImpl(loggingLevel, clientSecret)
}
val sdkMetadata = SdkMetadata(SDK_ID, SDK_VERSION)
val telemetry = Telemetry(SDK_ID, Telemetry.Library.CONFIDENCE, SDK_VERSION)
val engine = EventSenderEngineImpl.instance(
context,
clientSecret,
Expand All @@ -387,7 +401,7 @@ object ConfidenceFactory {
)
val flagApplierClient = FlagApplierClientImpl(
clientSecret,
sdkMetadata,
telemetry,
region,
dispatcher
)
Expand All @@ -399,7 +413,7 @@ object ConfidenceFactory {
.callTimeout(timeoutMillis, TimeUnit.MILLISECONDS)
.build(),
dispatcher = dispatcher,
sdkMetadata = sdkMetadata,
telemetry = telemetry,
debugLogger = debugLogger
)

Expand All @@ -419,7 +433,8 @@ object ConfidenceFactory {
flagResolver = flagResolver,
diskStorage = FileDiskStorage.create(context),
flagApplierClient = flagApplierClient,
debugLogger = debugLogger
debugLogger = debugLogger,
telemetry = telemetry
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.spotify.confidence

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 kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
Expand All @@ -17,6 +16,7 @@ import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import java.net.SocketTimeoutException

internal interface FlagResolver {
suspend fun resolve(flags: List<String>, context: Map<String, ConfidenceValue>): Result<FlagResolution>
Expand All @@ -26,7 +26,7 @@ internal class RemoteFlagResolver(
private val clientSecret: String,
private val region: ConfidenceRegion,
private val httpClient: OkHttpClient,
private val sdkMetadata: SdkMetadata,
private val telemetry: Telemetry,
private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
private val baseUrl: HttpUrl? = null,
private val debugLogger: DebugLogger? = null
Expand All @@ -38,18 +38,37 @@ internal class RemoteFlagResolver(
"application/json"
)
override suspend fun resolve(flags: List<String>, context: Map<String, ConfidenceValue>): Result<FlagResolution> {
val sdk = Sdk(sdkMetadata.sdkId, sdkMetadata.sdkVersion)
val sdk = telemetry.sdk
val request = ResolveFlagsRequest(flags.map { "flags/$it" }, context, clientSecret, false, sdk)

val response = withContext(dispatcher) {
val jsonRequest = Json.encodeToString(request)
val httpRequest = Request.Builder()
val requestBuilder = Request.Builder()
.url("${baseUrl()}/v1/flags:resolve")
.headers(headers)
.post(jsonRequest.toRequestBody())
.build()

httpClient.newCall(httpRequest).await().toResolveFlags()
telemetry.encodedHeaderValue()?.let { headerValue ->
requestBuilder.addHeader(Telemetry.HEADER_NAME, headerValue)
}

val httpRequest = requestBuilder.build()

val startTime = System.nanoTime()
var status = Telemetry.RequestStatus.SUCCESS
try {
val result = httpClient.newCall(httpRequest).await()
result.toResolveFlags()
} catch (e: SocketTimeoutException) {
status = Telemetry.RequestStatus.TIMEOUT
throw e
} catch (e: Exception) {
status = Telemetry.RequestStatus.ERROR
throw e
} finally {
val elapsedMs = (System.nanoTime() - startTime) / 1_000_000
telemetry.trackResolveLatency(elapsedMs, status)
}
}

return when (response) {
Expand Down
Loading
Loading