diff --git a/src/main/java/com/statsig/androidsdk/DynamicConfig.kt b/src/main/java/com/statsig/androidsdk/DynamicConfig.kt index 280b9f7..c864459 100644 --- a/src/main/java/com/statsig/androidsdk/DynamicConfig.kt +++ b/src/main/java/com/statsig/androidsdk/DynamicConfig.kt @@ -25,7 +25,7 @@ class DynamicConfig( apiDynamicConfig.value, apiDynamicConfig.ruleID, apiDynamicConfig.groupName, - apiDynamicConfig.secondaryExposures, + apiDynamicConfig.secondaryExposures ?: arrayOf(), apiDynamicConfig.isUserInExperiment, apiDynamicConfig.isExperimentActive, apiDynamicConfig.isDeviceBased, diff --git a/src/main/java/com/statsig/androidsdk/FeatureGate.kt b/src/main/java/com/statsig/androidsdk/FeatureGate.kt index c74ef1f..4d67603 100644 --- a/src/main/java/com/statsig/androidsdk/FeatureGate.kt +++ b/src/main/java/com/statsig/androidsdk/FeatureGate.kt @@ -22,7 +22,7 @@ class FeatureGate( apiFeatureGate.value, apiFeatureGate.ruleID, apiFeatureGate.groupName, - apiFeatureGate.secondaryExposures, + apiFeatureGate.secondaryExposures ?: arrayOf(), apiFeatureGate.idType, ) diff --git a/src/main/java/com/statsig/androidsdk/InitializeResponse.kt b/src/main/java/com/statsig/androidsdk/InitializeResponse.kt index b4c4018..aa2c4da 100644 --- a/src/main/java/com/statsig/androidsdk/InitializeResponse.kt +++ b/src/main/java/com/statsig/androidsdk/InitializeResponse.kt @@ -32,7 +32,7 @@ internal data class APIFeatureGate( @SerializedName("value") val value: Boolean = false, @SerializedName("rule_id") val ruleID: String = "", @SerializedName("group_name") val groupName: String? = null, - @SerializedName("secondary_exposures") val secondaryExposures: Array> = arrayOf(), + @SerializedName("secondary_exposures") val secondaryExposures: Array>? = arrayOf(), @SerializedName("id_type") val idType: String? = null, ) @@ -41,11 +41,11 @@ internal data class APIDynamicConfig( @SerializedName("value") val value: Map, @SerializedName("rule_id") val ruleID: String = "", @SerializedName("group_name") val groupName: String? = null, - @SerializedName("secondary_exposures") val secondaryExposures: Array> = arrayOf(), - @SerializedName("undelegated_secondary_exposures") val undelegatedSecondaryExposures: Array> = arrayOf(), + @SerializedName("secondary_exposures") val secondaryExposures: Array>? = arrayOf(), + @SerializedName("undelegated_secondary_exposures") val undelegatedSecondaryExposures: Array>? = arrayOf(), @SerializedName("is_device_based") val isDeviceBased: Boolean = false, @SerializedName("is_user_in_experiment") val isUserInExperiment: Boolean = false, @SerializedName("is_experiment_active") val isExperimentActive: Boolean = false, @SerializedName("allocated_experiment_name") val allocatedExperimentName: String? = null, - @SerializedName("explicit_parameters") val explicitParameters: Array = arrayOf(), + @SerializedName("explicit_parameters") val explicitParameters: Array? = arrayOf(), ) diff --git a/src/main/java/com/statsig/androidsdk/Layer.kt b/src/main/java/com/statsig/androidsdk/Layer.kt index fe15447..afe738f 100644 --- a/src/main/java/com/statsig/androidsdk/Layer.kt +++ b/src/main/java/com/statsig/androidsdk/Layer.kt @@ -30,8 +30,8 @@ class Layer internal constructor( apiDynamicConfig.value, apiDynamicConfig.ruleID, apiDynamicConfig.groupName, - apiDynamicConfig.secondaryExposures, - apiDynamicConfig.undelegatedSecondaryExposures, + apiDynamicConfig.secondaryExposures ?: arrayOf(), + apiDynamicConfig.undelegatedSecondaryExposures ?: arrayOf(), apiDynamicConfig.isUserInExperiment, apiDynamicConfig.isExperimentActive, apiDynamicConfig.isDeviceBased, diff --git a/src/test/java/com/statsig/androidsdk/SerializationTest.kt b/src/test/java/com/statsig/androidsdk/SerializationTest.kt new file mode 100644 index 0000000..54b58df --- /dev/null +++ b/src/test/java/com/statsig/androidsdk/SerializationTest.kt @@ -0,0 +1,24 @@ +package com.statsig.androidsdk + +import com.google.gson.Gson +import org.junit.Test + +/* +* Gson serialization and deserialization does not use default value +* set in data class when a field is missing +* */ +class SerializationTest { + val gson = Gson() + + @Test + fun testSerializeResponseWithIncomplete() { + val initializeResponseSkipFields = "{\"feature_gates\":{\"245595137\":{\"name\":\"245595137\",\"value\":true,\"rule_id\":\"1uj9J1jxY2jnBAChgGB1jR:0.00:35\",\"id_type\":\"userID\"}},\"dynamic_configs\":{\"2887220988\":{\"name\":\"2887220988\",\"value\":{\"num\": 13},\"rule_id\":\"prestart\",\"group\":\"prestart\",\"is_device_based\":false,\"id_type\":\"userID\",\"is_experiment_active\":true,\"is_user_in_experiment\":true}},\"layer_configs\":{},\"sdkParams\":{},\"has_updates\":true,\"time\":1717536742309,\"company_lcut\":1717536742309,\"hash_used\":\"djb2\"}" + val parsedResponse = gson.fromJson(initializeResponseSkipFields, InitializeResponse.SuccessfulInitializeResponse::class.java) + val gate = FeatureGate("some_gate", parsedResponse.featureGates!!.get("245595137")!!, EvaluationDetails(EvaluationReason.Error)) + val config = DynamicConfig("some_config", parsedResponse.configs!!.get("2887220988")!!, EvaluationDetails(EvaluationReason.Error)) + assert(gate.getValue()) + assert(gate.getSecondaryExposures().isEmpty()) + assert(config.getInt("num", 0) == 13) + assert(config.getSecondaryExposures().isEmpty()) + } +}