Skip to content

Commit

Permalink
Merge pull request #100 from growthbook/changelog/v0.5.5
Browse files Browse the repository at this point in the history
Changelog v0.5.5
  • Loading branch information
vazarkevych authored May 1, 2024
2 parents feec7ef + eac61a0 commit 1694c7c
Show file tree
Hide file tree
Showing 4 changed files with 815 additions and 283 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,11 @@ internal class GBExperimentEvaluator {
experimentKey = experiment.key,
experimentBucketVersion = experiment.bucketVersion ?: 0,
minExperimentBucketVersion = experiment.minBucketVersion ?: 0,
meta = experiment.meta ?: emptyList()
)
meta = experiment.meta ?: emptyList(),
expFallBackAttribute = experiment.fallBackAttribute,
expHashAttribute = experiment.hashAttribute,
attributeOverrides = attributeOverrides)

foundStickyBucket = variation >= 0
assigned = variation
stickyBucketVersionIsBlocked = versionIsBlocked ?: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,13 +379,54 @@ internal class GBUtils {
* Method to get actual Sticky Bucket assignments
*/
private fun getStickyBucketAssignments(
context: GBContext
context: GBContext,
expHashAttribute: String?,
expFallBackAttribute: String?,
attributeOverrides: Map<String, Any>
): Map<String, String> {
val mergedAssignments = mutableMapOf<String, String>()

val stickyBucketAssignmentDocs = context.stickyBucketAssignmentDocs ?: return emptyMap()

val (hashAttribute, hashValue) = getHashAttribute(
context = context,
attr = expHashAttribute,
fallback = null,
attributeOverrides = attributeOverrides
)

val hashKey = "$hashAttribute||$hashValue"

val (fallbackAttribute, fallbackValue) = getHashAttribute(
context = context,
attr = null,
fallback = expFallBackAttribute,
attributeOverrides = attributeOverrides
)

val fallbackKey = if (fallbackValue.isEmpty()) null else "$fallbackAttribute||$fallbackValue"

val leftOperand: String = context.stickyBucketAssignmentDocs
?.get("${expFallBackAttribute}||${attributeOverrides[expFallBackAttribute]}")
?.attributeValue.toString()
if (leftOperand != attributeOverrides[expFallBackAttribute].toString()) {
context.stickyBucketAssignmentDocs = emptyMap()
}

val mergedAssignments = mutableMapOf<String, String>()
context.stickyBucketAssignmentDocs?.values?.forEach { doc ->
mergedAssignments.putAll(doc.assignments)
}

fallbackKey?.let { fallbackKey ->
stickyBucketAssignmentDocs[fallbackKey]?.let { fallbackAssignments ->
mergedAssignments.putAll(fallbackAssignments.assignments)
}
}

stickyBucketAssignmentDocs[hashKey]?.let { hashAssignments ->
mergedAssignments.putAll(hashAssignments.assignments)
}

return mergedAssignments
}

Expand All @@ -397,10 +438,14 @@ internal class GBUtils {
experimentKey: String,
experimentBucketVersion: Int = 0,
minExperimentBucketVersion: Int = 0,
meta: List<GBVariationMeta> = emptyList()
meta: List<GBVariationMeta> = emptyList(),
expFallBackAttribute: String? = null,
expHashAttribute: String? = "id",
attributeOverrides: Map<String, Any>
): Pair<Int, Boolean?> {
val id = getStickyBucketExperimentKey(experimentKey, experimentBucketVersion)
val assignments = getStickyBucketAssignments(context)
val assignments = getStickyBucketAssignments(context = context,
expHashAttribute = expHashAttribute, expFallBackAttribute = expFallBackAttribute, attributeOverrides = attributeOverrides )

if (minExperimentBucketVersion > 0) {
for (version in 0..minExperimentBucketVersion) {
Expand All @@ -412,7 +457,7 @@ internal class GBUtils {
}
val variationKey = assignments[id] ?: return Pair(-1, null)
val variation = meta.indexOfFirst { it.key == variationKey }
return if (variation != -1) {
return if (0 <= variation) {
Pair(variation, null)
} else {
Pair(-1, null)
Expand Down Expand Up @@ -476,7 +521,7 @@ internal class GBUtils {

// if no match, try fallback
if (hashValue.isEmpty() && fallback != null) {
if (attributeOverrides[fallback] != JsonNull) {
if (attributeOverrides[fallback] != null) {
hashValue = attributeOverrides[fallback].toString()
} else if (context.attributes[fallback] != null) {
hashValue = context.attributes[fallback].toString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import com.sdk.growthbook.utils.toHashMap
import com.sdk.growthbook.evaluators.GBFeatureEvaluator
import com.sdk.growthbook.model.GBContext
import com.sdk.growthbook.stickybucket.GBStickyBucketServiceImp
import com.sdk.growthbook.utils.GBStickyAssignmentsDocument
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import org.junit.Before
Expand Down Expand Up @@ -60,21 +63,39 @@ class GBStickyBucketingFeatureTests {
if (testData.stickyBucketAssignmentDocs != null) {
gbContext.stickyBucketAssignmentDocs = testData.stickyBucketAssignmentDocs
}
val expectedExperimentResult: GBExperimentResultTest? = if (item[3] is JsonNull) {


val listActualStickyAssigmentsDoc = mutableListOf<GBStickyAssignmentsDocument>()

item[2].jsonArray.forEach {
listActualStickyAssigmentsDoc.add(
Json.decodeFromJsonElement(GBStickyAssignmentsDocument.serializer(), it)
)
}

val mapOfDocForContext = mutableMapOf<String, GBStickyAssignmentsDocument>()
for (doc in listActualStickyAssigmentsDoc) {
val key = "${doc.attributeName}||${doc.attributeValue}"
mapOfDocForContext[key] = doc
}

gbContext.stickyBucketAssignmentDocs = mapOfDocForContext

val expectedExperimentResult: GBExperimentResultTest? = if (item[4] is JsonNull) {
null
} else {
GBTestHelper.jsonParser.decodeFromJsonElement(
GBExperimentResultTest.serializer(),
item[3]
item[4]
)
}

val stickyAssigmentDocs = item[4].jsonObject.toHashMap()
val stickyAssigmentDocs = item[5].jsonObject.toHashMap()

val evaluator = GBFeatureEvaluator()
val actualExperimentResult = evaluator.evaluateFeature(
context = gbContext,
featureKey = item[2].jsonPrimitive.content,
featureKey = item[3].jsonPrimitive.content,
attributeOverrides = attributes
).experimentResult

Expand Down Expand Up @@ -112,11 +133,14 @@ class GBStickyBucketingFeatureTests {
println()
val status =
item[0].toString() +
"\nExpected Result - " + item[3] + " & " + stickyAssigmentDocs + "\n\n" +
"\nExpected Result - " + item[4] + " & " + stickyAssigmentDocs + "\n\n" +
"\nActual result - " + actualExperimentResult.toString() + " & " + gbContext.stickyBucketAssignmentDocs + "\n\n"

if (expectedExperimentResult?.value.toString() == actualExperimentResult?.value.toString()
&& stickyAssigmentDocs.size == gbContext.stickyBucketAssignmentDocs?.size) {
if (
expectedExperimentResult?.value.toString() == actualExperimentResult?.value.toString()
&&
stickyAssigmentDocs.size == gbContext.stickyBucketAssignmentDocs?.size
) {
passedScenarios.add(status)
} else {
failedScenarios.add(status)
Expand Down
Loading

0 comments on commit 1694c7c

Please sign in to comment.