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
12 changes: 11 additions & 1 deletion .github/workflows/kong.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ name: KONG

on:
workflow_dispatch:
inputs:
kong-branch:
description: "Kong branch name"
type: string
required: false
pull_request:
branches: [main]
push:
Expand All @@ -19,7 +24,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Get KONG
run: git clone https://oauth2:$repo_pat@github.com/statsig-io/kong.git .
run: |
if [[ ${{ github.event_name }} == "workflow_dispatch" ]]; then
git clone -b ${{ inputs.kong-branch }} https://oauth2:$repo_pat@github.com/statsig-io/kong.git .
else
git clone https://oauth2:$repo_pat@github.com/statsig-io/kong.git .
fi

- name: Install Deps
run: npm install
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ repositories {

configure<org.jlleitschuh.gradle.ktlint.KtlintExtension> {
verbose.set(true)
disabledRules.set(setOf("no-wildcard-imports"))
}

dependencies {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ RELEASE_SIGNING_ENABLED=true

GROUP=com.statsig.serversdk
POM_ARTIFACT_ID=serversdk
VERSION_NAME=1.6.1
VERSION_NAME=1.6.2

POM_NAME=Statsig Server SDK
POM_DESCRIPTION=A feature gating and a/b testing library for statsig
Expand Down
6 changes: 1 addition & 5 deletions src/main/kotlin/com/statsig/sdk/ClientInitializeFormatter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,7 @@ internal data class ClientInitializeResponse(
fun toMap(): Map<String, Any> {
val gson = Gson()
val json = gson.toJson(this)
return try {
return gson.fromJson(json, object : TypeToken<Map<String, Any>>() {}.type)
} catch (e: Exception) {
emptyMap()
}
return gson.fromJson(json, object : TypeToken<Map<String, Any>>() {}.type)
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/com/statsig/sdk/ErrorBoundary.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import java.net.URI
import java.net.URLEncoder
import java.nio.charset.StandardCharsets

internal class ErrorBoundary(private val apiKey: String, private val options: StatsigOptions) {
internal class ErrorBoundary(private val apiKey: String, private val options: StatsigOptions, private val statsigMetadata: StatsigMetadata) {
internal var uri = URI("https://statsigapi.net/v1/sdk_exception")
private val seen = HashSet<String>()
private val maxInfoLength = 3000
Expand Down Expand Up @@ -68,7 +68,7 @@ internal class ErrorBoundary(private val apiKey: String, private val options: St
"tag": "$tag",
"exception": "${ex.javaClass.name}",
"info": "$safeInfo",
"statsigMetadata": ${StatsigMetadata.asJson()}
"statsigMetadata": ${statsigMetadata.asJson()}
}
""".trimIndent()
val req =
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/com/statsig/sdk/Evaluator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ internal class Evaluator(
private val statsigScope: CoroutineScope,
private val errorBoundary: ErrorBoundary,
private val diagnostics: Diagnostics,
private val statsigMetadata: StatsigMetadata,
) {
private var specStore: SpecStore
private val uaParser: Parser by lazy {
Expand All @@ -55,7 +56,7 @@ internal class Evaluator(

init {
CountryLookup.initialize()
specStore = SpecStore(this.network, this.options, StatsigMetadata(), statsigScope, errorBoundary, diagnostics)
specStore = SpecStore(this.network, this.options, statsigMetadata, statsigScope, errorBoundary, diagnostics)
network.setDiagnostics(diagnostics)

statsigScope.launch {
Expand Down
7 changes: 3 additions & 4 deletions src/main/kotlin/com/statsig/sdk/SpecStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,8 @@ internal class SpecStore constructor(
this.layerConfigs = newLayerConfigs
this.experimentToLayer = newExperimentToLayer
this.lastUpdateTime = downloadedConfig.time
if (downloadedConfig.sdkKeysToAppIDs != null) {
this.sdkKeysToAppIDs = downloadedConfig.sdkKeysToAppIDs
}
this.sdkKeysToAppIDs = downloadedConfig.sdkKeysToAppIDs ?: mapOf()

if (downloadedConfig.diagnostics != null) {
diagnostics.setSamplingRate(downloadedConfig.diagnostics)
}
Expand Down Expand Up @@ -329,7 +328,7 @@ internal class SpecStore constructor(
}

fun getAppIDFromKey(clientSDKKey: String): String? {
return this.sdkKeysToAppIDs.get(clientSDKKey)
return this.sdkKeysToAppIDs[clientSDKKey]
}

private suspend fun initializeSpecs() {
Expand Down
29 changes: 17 additions & 12 deletions src/main/kotlin/com/statsig/sdk/Statsig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ class Statsig {
serverSecret: String,
options: StatsigOptions,
) {
if (!::statsigServer.isInitialized) { // Quick check without synchronization
if (!isInitialized()) { // Quick check without synchronization
synchronized(this) {
if (!::statsigServer.isInitialized
if (!isInitialized()
) { // Secondary check in case another thread already created the default server
statsigServer = StatsigServer.create(serverSecret, options)
statsigServer = StatsigServer.create()
}
}
statsigServer.initialize()
statsigServer.initialize(serverSecret, options)
}
}

Expand Down Expand Up @@ -241,7 +241,7 @@ class Statsig {
/**
* Sets a value to be returned for the given dynamic config/experiment instead of the actual evaluated value.
*
* @param configName The name of the dynamic config or experiment to be overriden
* @param configName The name of the dynamic config or experiment to be overridden
* @param configValue The value that will be returned
*/
@JvmStatic
Expand Down Expand Up @@ -359,14 +359,14 @@ class Statsig {
serverSecret: String,
options: StatsigOptions = StatsigOptions(),
): CompletableFuture<Void?> {
if (!::statsigServer.isInitialized) { // Quick check without synchronization
if (!isInitialized()) { // Quick check without synchronization
synchronized(this) {
if (!::statsigServer.isInitialized
if (!isInitialized()
) { // Secondary check in case another thread already created the default server
statsigServer = StatsigServer.create(serverSecret, options)
statsigServer = StatsigServer.create()
}
}
return statsigServer.initializeAsync()
return statsigServer.initializeAsync(serverSecret, options)
}
return CompletableFuture.completedFuture(null)
}
Expand Down Expand Up @@ -631,12 +631,17 @@ class Statsig {
runBlocking { statsigServer.shutdown() }
}

@JvmStatic
fun isInitialized(): Boolean {
return ::statsigServer.isInitialized && statsigServer.initialized
}

private fun checkInitialized(): Boolean {
if (!::statsigServer.isInitialized) {
val initialized = isInitialized()
if (!initialized) {
println("Call and wait for initialize to complete before calling SDK methods.")
return false
}
return true
return initialized
}
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/com/statsig/sdk/StatsigEvent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ internal data class StatsigEvent(
@SerializedName("value") val eventValue: Any? = null,
@SerializedName("metadata") var eventMetadata: Map<String, String>? = null,
@SerializedName("user") var user: StatsigUser? = null,
@SerializedName("statsigMetadata") val statsigMetadata: Map<String, String>? = null,
@SerializedName("statsigMetadata") val statsigMetadata: StatsigMetadata? = null,
@SerializedName("secondaryExposures") val secondaryExposures: ArrayList<Map<String, String>>? = arrayListOf(),
@SerializedName("time") val time: Long? = Utils.getTimeInMillis(),
) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/com/statsig/sdk/StatsigLogger.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ internal fun safeAddEvaluationToEvent(evaluationDetails: EvaluationDetails?, met
internal class StatsigLogger(
private val coroutineScope: CoroutineScope,
private val network: StatsigNetwork,
private val statsigMetadata: Map<String, String>,
private val statsigMetadata: StatsigMetadata,
) {

private val executor = Executors.newSingleThreadExecutor()
Expand Down
42 changes: 19 additions & 23 deletions src/main/kotlin/com/statsig/sdk/StatsigMetadata.kt
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
package com.statsig.sdk

import com.google.gson.GsonBuilder
import com.google.gson.ToNumberPolicy
import com.google.gson.annotations.SerializedName
import java.util.Properties
import java.util.UUID

private const val VERSION = "1.6.1"
private const val VERSION = "1.6.2"

internal class StatsigMetadata {
companion object {
private val version =
try {
val properties = Properties()
properties.load(
StatsigMetadata::class.java.getResourceAsStream("/statsigsdk.properties"),
)
properties.getProperty("version")
} catch (e: Exception) {
VERSION
}

fun asMap(): Map<String, String> {
return mapOf("sdkType" to "java-server", "sdkVersion" to version)
}

fun asJson(): String {
val map = asMap()
val values = map.map { "\"${it.key}\":\"${it.value}\"" }
return "{${values.joinToString(",")}}"
}
internal data class StatsigMetadata(@SerializedName("sdkType") var sdkType: String = "java-server", @SerializedName("sessionID") var sessionID: String = UUID.randomUUID().toString(), @SerializedName("languageVersion") var languageVersion: String = System.getProperty("java.version"), @SerializedName("exposureLoggingDisabled") var exposureLoggingDisabled: Boolean? = null) {
@SerializedName("sdkVersion")
var sdkVersion: String = try {
val properties = Properties()
properties.load(
StatsigMetadata::class.java.getResourceAsStream("/statsigsdk.properties"),
)
properties.getProperty("version")
} catch (e: Exception) {
VERSION
}
fun asJson(): String {
val gson = GsonBuilder().setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE).create()
return gson.toJson(this)
}
}
19 changes: 10 additions & 9 deletions src/main/kotlin/com/statsig/sdk/StatsigNetwork.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private const val MS_IN_S: Long = 1000
internal class StatsigNetwork(
private val sdkKey: String,
private val options: StatsigOptions,
private val statsigMetadata: Map<String, String>,
private val statsigMetadata: StatsigMetadata,
private val errorBoundary: ErrorBoundary,
private val backoffMultiplier: Int = BACKOFF_MULTIPLIER,
) {
Expand Down Expand Up @@ -60,8 +60,8 @@ internal class StatsigNetwork(
.addHeader("STATSIG-API-KEY", sdkKey)
.addHeader("STATSIG-CLIENT-TIME", System.currentTimeMillis().toString())
.addHeader("STATSIG-SERVER-SESSION-ID", serverSessionID)
.addHeader("STATSIG-SDK-TYPE", statsigMetadata["sdkType"] ?: "")
.addHeader("STATSIG-SDK-VERSION", statsigMetadata["sdkVersion"] ?: "")
.addHeader("STATSIG-SDK-TYPE", statsigMetadata.sdkType)
.addHeader("STATSIG-SDK-VERSION", statsigMetadata.sdkVersion)
.method(original.method, original.body)
.build()
it.proceed(request)
Expand Down Expand Up @@ -91,12 +91,12 @@ internal class StatsigNetwork(
}

suspend fun checkGate(user: StatsigUser?, gateName: String, disableExposureLogging: Boolean): ConfigEvaluation {
val exposureLoggingMap = mapOf("exposureLoggingDisabled" to disableExposureLogging)
statsigMetadata.exposureLoggingDisabled = disableExposureLogging
val bodyJson = gson.toJson(
mapOf(
"gateName" to gateName,
"user" to user,
"statsigMetadata" to statsigMetadata + exposureLoggingMap,
"statsigMetadata" to statsigMetadata,
),
)
val requestBody: RequestBody = bodyJson.toRequestBody(json)
Expand All @@ -117,14 +117,15 @@ internal class StatsigNetwork(
}

suspend fun getConfig(user: StatsigUser?, configName: String, disableExposureLogging: Boolean): ConfigEvaluation {
val exposureLoggingMap = mapOf("exposureLoggingDisabled" to disableExposureLogging)
statsigMetadata.exposureLoggingDisabled = disableExposureLogging
val bodyJson = gson.toJson(
mapOf(
"configName" to configName,
"user" to user,
"statsigMetadata" to statsigMetadata + exposureLoggingMap,
"statsigMetadata" to statsigMetadata,
),
)
statsigMetadata.exposureLoggingDisabled = null
val requestBody: RequestBody = bodyJson.toRequestBody(json)

val request: Request = Request.Builder()
Expand Down Expand Up @@ -196,13 +197,13 @@ internal class StatsigNetwork(
}
}

suspend fun postLogs(events: List<StatsigEvent>, statsigMetadata: Map<String, String>) {
suspend fun postLogs(events: List<StatsigEvent>, statsigMetadata: StatsigMetadata) {
retryPostLogs(events, statsigMetadata, 5, 1)
}

suspend fun retryPostLogs(
events: List<StatsigEvent>,
statsigMetadata: Map<String, String>,
statsigMetadata: StatsigMetadata,
retries: Int,
backoff: Int,
) {
Expand Down
Loading