All Downloads are FREE. Search and download functionalities are using the official Maven repository.

.experiment-jvm-server.1.2.0-beta.8.source-code.LocalEvaluationClient.kt Maven / Gradle / Ivy

@file:OptIn(ExperimentalApi::class)

package com.amplitude.experiment

import com.amplitude.Amplitude
import com.amplitude.experiment.assignment.AmplitudeAssignmentService
import com.amplitude.experiment.assignment.Assignment
import com.amplitude.experiment.assignment.AssignmentService
import com.amplitude.experiment.assignment.InMemoryAssignmentFilter
import com.amplitude.experiment.cohort.CohortStorage
import com.amplitude.experiment.cohort.InMemoryCohortStorage
import com.amplitude.experiment.cohort.ProxyCohortMembershipApi
import com.amplitude.experiment.cohort.ProxyCohortStorage
import com.amplitude.experiment.deployment.DeploymentRunner
import com.amplitude.experiment.evaluation.EvaluationEngine
import com.amplitude.experiment.evaluation.EvaluationEngineImpl
import com.amplitude.experiment.evaluation.FlagConfig
import com.amplitude.experiment.evaluation.serialization.SerialVariant
import com.amplitude.experiment.flag.FlagConfigStorage
import com.amplitude.experiment.flag.HybridFlagConfigApi
import com.amplitude.experiment.flag.InMemoryFlagConfigStorage
import com.amplitude.experiment.util.LocalEvaluationMetricsWrapper
import com.amplitude.experiment.util.Logger
import com.amplitude.experiment.util.getCohortIds
import com.amplitude.experiment.util.toSerialExperimentUser
import com.amplitude.experiment.util.toVariant
import com.amplitude.experiment.util.wrapMetrics
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient

class LocalEvaluationClient internal constructor(
    private val deploymentKey: String,
    private val config: LocalEvaluationConfig = LocalEvaluationConfig(),
) {

    private val metricsWrapper = LocalEvaluationMetricsWrapper(config.metrics)
    private val evaluation: EvaluationEngine = EvaluationEngineImpl()
    private val httpClient = OkHttpClient()
    private val assignmentService: AssignmentService? = createAssignmentService()
    private val cohortStorage: CohortStorage = createCohortStorage()
    private val flagConfigStorage: FlagConfigStorage = InMemoryFlagConfigStorage()
    private val deploymentRunner = DeploymentRunner(
        config,
        httpClient,
        HybridFlagConfigApi(
            deploymentKey,
            config.serverUrl.toHttpUrl(),
            config.proxyConfiguration?.proxyUrl?.toHttpUrl(),
            httpClient,
        ),
        flagConfigStorage,
        cohortStorage,
        metricsWrapper
    )

    fun start() {
        deploymentRunner.start()
    }

    fun stop() {
        deploymentRunner.stop()
    }

    @JvmOverloads
    fun evaluate(user: ExperimentUser, flagKeys: List = listOf()): Map {
        val flagConfigs = flagConfigStorage.getFlagConfigs()
        val enrichedUser = enrichUser(user, flagConfigs)
        val flagResults = wrapMetrics(
            metric = metricsWrapper::onEvaluation,
            failure = metricsWrapper::onEvaluationFailure,
        ) {
            evaluation.evaluate(flagConfigs, enrichedUser.toSerialExperimentUser().convert())
        }
        Logger.d("evaluate - user=$enrichedUser, result=$flagResults")
        assignmentService?.track(Assignment(user, flagResults))
        return flagResults.filter { entry ->
            val isVariant = !entry.value.isDefaultVariant
            val isIncluded = (flagKeys.isEmpty() || flagKeys.contains(entry.key))
            val isDeployed = entry.value.deployed
            isVariant && isIncluded && isDeployed
        }.map { entry ->
            entry.key to SerialVariant(entry.value.variant).toVariant()
        }.toMap()
    }

    private fun enrichUser(user: ExperimentUser, flagConfigs: List): ExperimentUser {
        val cohortIds = flagConfigs.getCohortIds()
        return if (user.userId == null || cohortIds.isEmpty()) {
            user
        } else {
            user.copyToBuilder().apply {
                cohortIds(cohortStorage.getCohortsForUser(user.userId, cohortIds))
            }.build()
        }
    }

    private fun createAssignmentService(): AssignmentService? {
        if (config.assignmentConfiguration == null) return null
        return AmplitudeAssignmentService(
            Amplitude.getInstance("experiment").apply {
                setEventUploadThreshold(config.assignmentConfiguration.eventUploadThreshold)
                setEventUploadPeriodMillis(config.assignmentConfiguration.eventUploadPeriodMillis)
                useBatchMode(config.assignmentConfiguration.useBatchMode)
                init(config.assignmentConfiguration.apiKey)
            },
            InMemoryAssignmentFilter(config.assignmentConfiguration.filterCapacity),
            metricsWrapper
        )
    }

    private fun createCohortStorage(): CohortStorage {
        if (config.proxyConfiguration == null) return InMemoryCohortStorage()
        return ProxyCohortStorage(config.proxyConfiguration, ProxyCohortMembershipApi(deploymentKey, config.proxyConfiguration.proxyUrl.toHttpUrl(), httpClient))
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy