Skip to content

Commit

Permalink
Merge pull request #93 from growthbook/release/1.1.45
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
vazarkevych authored Apr 18, 2024
2 parents 8e69307 + c3b8be2 commit 842ec82
Show file tree
Hide file tree
Showing 35 changed files with 544 additions and 193 deletions.
2 changes: 1 addition & 1 deletion GrowthBook/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = "io.growthbook.sdk"
version = "1.1.44"
version = "1.1.45"

kotlin {

Expand Down
154 changes: 121 additions & 33 deletions GrowthBook/src/commonMain/kotlin/com/sdk/growthbook/GrowthBookSDK.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.sdk.growthbook

import com.sdk.growthbook.Network.NetworkDispatcher
import com.sdk.growthbook.Utils.Crypto
import com.sdk.growthbook.Utils.GBCacheRefreshHandler
import com.sdk.growthbook.Utils.GBError
import com.sdk.growthbook.Utils.GBFeatures
import com.sdk.growthbook.Utils.GBUtils.Companion.refreshStickyBuckets
import com.sdk.growthbook.Utils.Resource
import com.sdk.growthbook.Utils.getFeaturesFromEncryptedFeatures
import com.sdk.growthbook.network.NetworkDispatcher
import com.sdk.growthbook.utils.Crypto
import com.sdk.growthbook.utils.GBCacheRefreshHandler
import com.sdk.growthbook.utils.GBError
import com.sdk.growthbook.utils.GBFeatures
import com.sdk.growthbook.utils.GBRemoteEvalParams
import com.sdk.growthbook.utils.GBUtils.Companion.refreshStickyBuckets
import com.sdk.growthbook.utils.Resource
import com.sdk.growthbook.utils.getFeaturesFromEncryptedFeatures
import com.sdk.growthbook.evaluators.GBExperimentEvaluator
import com.sdk.growthbook.evaluators.GBFeatureEvaluator
import com.sdk.growthbook.features.FeaturesDataModel
Expand All @@ -29,15 +30,18 @@ typealias GBTrackingCallback = (GBExperiment, GBExperimentResult) -> Unit
* HostURL - Server URL
* UserAttributes - User Attributes
* Tracking Callback - Track Events for Experiments
* EncryptionKey - Encryption key if you intend to use data encryption.
* EncryptionKey - Encryption key if you intend to use data encryption
* Network Dispatcher - Network Dispatcher
* Remote eval - Whether to use Remote Evaluation
*/
abstract class SDKBuilder(
val apiKey: String,
val hostURL: String,
val attributes: Map<String, Any>,
val trackingCallback: GBTrackingCallback,
val encryptionKey: String?,
val networkDispatcher: NetworkDispatcher
val networkDispatcher: NetworkDispatcher,
val remoteEval: Boolean
) {
internal var qaMode: Boolean = false
internal var forcedVariations: Map<String, Int> = HashMap()
Expand Down Expand Up @@ -81,7 +85,9 @@ abstract class SDKBuilder(
* UserAttributes - User Attributes
* Features - GrowthBook Features Map - Synced via Web API / Web Hooks
* Tracking Callback - Track Events for Experiments
* EncryptionKey - Encryption key if you intend to use data encryption.
* EncryptionKey - Encryption key if you intend to use data encryption
* Network Dispatcher - Network Dispatcher
* Remote eval - Whether to use Remote Evaluation
*/
class GBSDKBuilderJAVA(
apiKey: String,
Expand All @@ -90,10 +96,11 @@ class GBSDKBuilderJAVA(
val features: GBFeatures,
trackingCallback: GBTrackingCallback,
encryptionKey: String?,
networkDispatcher: NetworkDispatcher
networkDispatcher: NetworkDispatcher,
remoteEval: Boolean = false,
) : SDKBuilder(
apiKey, hostURL,
attributes, trackingCallback, encryptionKey, networkDispatcher
attributes, trackingCallback, encryptionKey, networkDispatcher, remoteEval
) {
/**
* Initialize the JAVA SDK
Expand All @@ -109,7 +116,8 @@ class GBSDKBuilderJAVA(
qaMode = qaMode,
forcedVariations = forcedVariations,
trackingCallback = trackingCallback,
encryptionKey = encryptionKey
encryptionKey = encryptionKey,
remoteEval = remoteEval,
)

return GrowthBookSDK(gbContext, null, networkDispatcher, features)
Expand All @@ -122,18 +130,21 @@ class GBSDKBuilderJAVA(
* HostURL - Server URL
* UserAttributes - User Attributes
* Tracking Callback - Track Events for Experiments
* EncryptionKey - Encryption key if you intend to use data encryption.
* EncryptionKey - Encryption key if you intend to use data encryption
* Network Dispatcher - Network Dispatcher
* Remote eval - Whether to use Remote Evaluation
*/
class GBSDKBuilder(
apiKey: String,
hostURL: String,
attributes: Map<String, Any>,
trackingCallback: GBTrackingCallback,
encryptionKey: String? = null,
networkDispatcher: NetworkDispatcher
networkDispatcher: NetworkDispatcher,
remoteEval: Boolean = false,
) : SDKBuilder(
apiKey, hostURL,
attributes, trackingCallback, encryptionKey, networkDispatcher
attributes, trackingCallback, encryptionKey, networkDispatcher, remoteEval
) {

private var refreshHandler: GBCacheRefreshHandler? = null
Expand All @@ -147,7 +158,7 @@ class GBSDKBuilder(
}

/**
* Initialize the JAVA SDK
* Initialize the Kotlin SDK
*/
@DelicateCoroutinesApi
override fun initialize(): GrowthBookSDK {
Expand All @@ -160,10 +171,15 @@ class GBSDKBuilder(
qaMode = qaMode,
forcedVariations = forcedVariations,
trackingCallback = trackingCallback,
encryptionKey = encryptionKey
encryptionKey = encryptionKey,
remoteEval = remoteEval
)

return GrowthBookSDK(gbContext, refreshHandler, networkDispatcher, features = null)
return GrowthBookSDK(
gbContext,
refreshHandler,
networkDispatcher
)
}
}

Expand All @@ -176,7 +192,8 @@ class GrowthBookSDK() : FeaturesFlowDelegate {
private var refreshHandler: GBCacheRefreshHandler? = null
private lateinit var networkDispatcher: NetworkDispatcher
private lateinit var featuresViewModel: FeaturesViewModel
private lateinit var attributeOverrides: Map<String, Any>
private var attributeOverrides: Map<String, Any> = emptyMap()
private var forcedFeatures: Map<String, Any> = emptyMap()

//@ThreadLocal
internal companion object {
Expand All @@ -188,7 +205,7 @@ class GrowthBookSDK() : FeaturesFlowDelegate {
context: GBContext,
refreshHandler: GBCacheRefreshHandler?,
networkDispatcher: NetworkDispatcher,
features: GBFeatures? = null,
features: GBFeatures? = null
) : this() {
gbContext = context
this.refreshHandler = refreshHandler
Expand All @@ -210,14 +227,19 @@ class GrowthBookSDK() : FeaturesFlowDelegate {
refreshCache()
}
this.attributeOverrides = gbContext.attributes
refreshStickyBucketService()
}

/**
* Manually Refresh Cache
*/
@DelicateCoroutinesApi
fun refreshCache() {
featuresViewModel.fetchFeatures()
if (gbContext.remoteEval) {
refreshForRemoteEval()
} else {
featuresViewModel.fetchFeatures()
}
}

/**
Expand All @@ -243,6 +265,9 @@ class GrowthBookSDK() : FeaturesFlowDelegate {
return gbContext.features
}

/**
* Delegate that set to Context successfully fetched features
*/
override fun featuresFetchedSuccessfully(features: GBFeatures, isRemote: Boolean) {
gbContext.features = features
if (isRemote) {
Expand All @@ -251,21 +276,27 @@ class GrowthBookSDK() : FeaturesFlowDelegate {
}

/**
* The setEncryptedFeatures method takes an encrypted string with an encryption key and then decrypts it with the default method of decrypting or with a method of decrypting from the user
* The setEncryptedFeatures method takes an encrypted string with an encryption key
* and then decrypts it with the default method of decrypting
* or with a method of decrypting from the user
*/
fun setEncryptedFeatures(
encryptedString: String,
encryptionKey: String,
subtleCrypto: Crypto?
) {
val feature = getFeaturesFromEncryptedFeatures(
encryptedString = encryptedString,
encryptionKey = encryptionKey,
subtleCrypto = subtleCrypto
)
gbContext.features =
getFeaturesFromEncryptedFeatures(
encryptedString = encryptedString,
encryptionKey = encryptionKey,
subtleCrypto = subtleCrypto
) ?: return
feature ?: return
}

/**
* Delegate which inform that fetching features failed
*/
override fun featuresFetchFailed(error: GBError, isRemote: Boolean) {

if (isRemote) {
Expand Down Expand Up @@ -302,6 +333,20 @@ class GrowthBookSDK() : FeaturesFlowDelegate {
)
}

/**
* The setForcedFeatures method setup the Map of user's (forced) features
*/
fun setForcedFeatures(forcedFeatures: Map<String, Any>) {
this.forcedFeatures = forcedFeatures
}

/**
* The getForcedFeatures method for mapping model object for request's body type
*/
fun getForcedFeatures(): List<List<Any>> {
return this.forcedFeatures.map { listOf(it.key, it.value) }
}

/**
* The setAttributes method replaces the Map of user attributes that are used to assign variations
*/
Expand All @@ -310,23 +355,66 @@ class GrowthBookSDK() : FeaturesFlowDelegate {
refreshStickyBucketService()
}

/**
* The setAttributeOverrides method replaces the Map of user overrides attribute
* that are used for Sticky Bucketing
*/
fun setAttributeOverrides(overrides: Map<String, Any>) {
attributeOverrides = overrides
refreshStickyBucketService()
if (gbContext.stickyBucketService != null) {
refreshStickyBucketService()
}
refreshForRemoteEval()
}

/**
* The setForcedVariations method setup the Map of user's (forced) variations
* to assign a specific variation (used for QA)
*/
fun setForcedVariations(forcedVariations: Map<String, Any>) {
gbContext.forcedVariations = forcedVariations
refreshForRemoteEval()
}

/**
* Delegate that call refresh Sticky Bucket Service
* after success fetched features
*/
override fun featuresAPIModelSuccessfully(model: FeaturesDataModel) {
refreshStickyBucketService(dataModel = model)
}

/**
* Method for update latest attributes
*/
private fun refreshStickyBucketService(dataModel: FeaturesDataModel? = null) {
if (gbContext.stickyBucketService != null) {
val featureEvaluator = GBFeatureEvaluator().evaluateFeature(
GBFeatureEvaluator().evaluateFeature(
context = gbContext,
featureKey = "",
attributeOverrides = attributeOverrides
)
refreshStickyBuckets(context = gbContext, data = dataModel, attributeOverrides = attributeOverrides)
refreshStickyBuckets(
context = gbContext,
data = dataModel,
attributeOverrides = attributeOverrides
)
}
}

/**
* Method for sending request evaluate features remotely
*/
@OptIn(DelicateCoroutinesApi::class)
private fun refreshForRemoteEval() {
if (!gbContext.remoteEval) {
return
}
val payload = GBRemoteEvalParams(
gbContext.attributes,
this.getForcedFeatures(),
gbContext.forcedVariations
)
featuresViewModel.fetchFeatures(gbContext.remoteEval, payload)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.sdk.growthbook.evaluators

import com.sdk.growthbook.Utils.GBCondition
import com.sdk.growthbook.Utils.GBUtils
import com.sdk.growthbook.utils.GBCondition
import com.sdk.growthbook.utils.GBUtils
import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package com.sdk.growthbook.evaluators

import com.sdk.growthbook.Utils.GBUtils
import com.sdk.growthbook.Utils.toJsonElement
import com.sdk.growthbook.utils.GBUtils
import com.sdk.growthbook.utils.toJsonElement
import com.sdk.growthbook.model.GBContext
import com.sdk.growthbook.model.GBExperiment
import com.sdk.growthbook.model.GBExperimentResult
import com.sdk.growthbook.model.GBFeatureSource
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject

/**
Expand Down Expand Up @@ -163,11 +162,11 @@ internal class GBExperimentEvaluator {
)
}
val evalObj = parentResult.value?.let {
JsonObject(mapOf("value" to it))
} ?: JsonObject(emptyMap())
mapOf("value" to GBUtils.convertToPrimitiveIfPossible(it))
} ?: emptyMap()

val evalCondition = GBConditionEvaluator().evalCondition(
attributes = evalObj,
attributes = evalObj.toJsonElement(),
conditionObj = parentCondition.condition
)

Expand Down
Loading

0 comments on commit 842ec82

Please sign in to comment.