diff --git a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt index 47cc4b3..99b03a7 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/Flagsmith.kt @@ -106,12 +106,12 @@ class Flagsmith constructor( const val DEFAULT_ANALYTICS_FLUSH_PERIOD_SECONDS = 10 } - fun getFeatureFlags(identity: String? = null, result: (Result>) -> Unit) { + fun getFeatureFlags(identity: String? = null, transient: Boolean? = null, result: (Result>) -> Unit) { // Save the last used identity as we'll refresh with this if we get update events lastUsedIdentity = identity if (identity != null) { - retrofit.getIdentityFlagsAndTraits(identity).enqueueWithResult { res -> + retrofit.getIdentityFlagsAndTraits(identity, transient).enqueueWithResult { res -> flagUpdateFlow.tryEmit(res.getOrNull()?.flags ?: emptyList()) result(res.map { it.flags }) } @@ -139,40 +139,64 @@ class Flagsmith constructor( result(res.map { flag -> flag?.featureStateValue }) } - fun getTrait(id: String, identity: String, result: (Result) -> Unit) = - retrofit.getIdentityFlagsAndTraits(identity).enqueueWithResult { res -> + fun getTrait( + id: String, + identity: String, + transient: Boolean? = null, + result: (Result) -> Unit + ) { + retrofit.getIdentityFlagsAndTraits(identity, transient).enqueueWithResult { res -> result(res.map { value -> value.traits.find { it.key == id } }) }.also { lastUsedIdentity = identity } + } - fun getTraits(identity: String, result: (Result>) -> Unit) = - retrofit.getIdentityFlagsAndTraits(identity).enqueueWithResult { res -> + fun getTraits( + identity: String, + transient: Boolean? = null, + result: (Result>) -> Unit + ) { + retrofit.getIdentityFlagsAndTraits(identity, transient).enqueueWithResult { res -> result(res.map { it.traits }) }.also { lastUsedIdentity = identity } + } - fun setTrait(trait: Trait, identity: String, result: (Result) -> Unit) = - retrofit.postTraits(IdentityAndTraits(identity, listOf(trait))) + fun setTrait(trait: Trait, identity: String, transient: Boolean? = null, result: (Result) -> Unit) { + val identityAndTraits = if (transient != null) { + IdentityAndTraits(identity, listOf(trait), transient) + } else { + IdentityAndTraits(identity, listOf(trait)) + } + retrofit.postTraits(identityAndTraits) .enqueueWithResult(result = { result(it.map { response -> TraitWithIdentity( key = response.traits.first().key, traitValue = response.traits.first().traitValue, - identity = Identity(identity) + identity = Identity(identity), + transient = response.traits.first().transient )}) }) + } - fun setTraits(traits: List, identity: String, result: (Result>) -> Unit) { - retrofit.postTraits(IdentityAndTraits(identity, traits)).enqueueWithResult(result = { + fun setTraits(traits: List, identity: String, transient: Boolean? = null, result: (Result>) -> Unit) { + val identityAndTraits = if (transient != null) { + IdentityAndTraits(identity, traits, transient) + } else { + IdentityAndTraits(identity, traits) + } + retrofit.postTraits(identityAndTraits).enqueueWithResult(result = { result(it.map { response -> response.traits.map { trait -> TraitWithIdentity( key = trait.key, traitValue = trait.traitValue, - identity = Identity(identity) + identity = Identity(identity), + transient = trait.transient ) }}) }) } - fun getIdentity(identity: String, result: (Result) -> Unit) = - retrofit.getIdentityFlagsAndTraits(identity).enqueueWithResult(defaults = null, result = result) + fun getIdentity(identity: String, transient: Boolean? = null, result: (Result) -> Unit) = + retrofit.getIdentityFlagsAndTraits(identity, transient).enqueueWithResult(defaults = null, result = result) .also { lastUsedIdentity = identity } fun clearCache() { diff --git a/FlagsmithClient/src/main/java/com/flagsmith/entities/Identity.kt b/FlagsmithClient/src/main/java/com/flagsmith/entities/Identity.kt index 93e81f6..a4ad0b5 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/entities/Identity.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/entities/Identity.kt @@ -1,5 +1,5 @@ package com.flagsmith.entities data class Identity( - val identifier: String + val identifier: String, ) \ No newline at end of file diff --git a/FlagsmithClient/src/main/java/com/flagsmith/entities/IdentityAndTraits.kt b/FlagsmithClient/src/main/java/com/flagsmith/entities/IdentityAndTraits.kt index c626f5b..49f28d2 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/entities/IdentityAndTraits.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/entities/IdentityAndTraits.kt @@ -4,5 +4,6 @@ import com.google.gson.annotations.SerializedName data class IdentityAndTraits( @SerializedName(value = "identifier") val identifier: String, - @SerializedName(value = "traits") val traits: List + @SerializedName(value = "traits") val traits: List, + @SerializedName(value = "transient") val transient: Boolean? = null ) \ No newline at end of file diff --git a/FlagsmithClient/src/main/java/com/flagsmith/entities/IdentityFlagsAndTraits.kt b/FlagsmithClient/src/main/java/com/flagsmith/entities/IdentityFlagsAndTraits.kt index 365ccdf..2305a6f 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/entities/IdentityFlagsAndTraits.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/entities/IdentityFlagsAndTraits.kt @@ -2,5 +2,5 @@ package com.flagsmith.entities data class IdentityFlagsAndTraits( val flags: ArrayList, - val traits: ArrayList + val traits: ArrayList, ) \ No newline at end of file diff --git a/FlagsmithClient/src/main/java/com/flagsmith/entities/Trait.kt b/FlagsmithClient/src/main/java/com/flagsmith/entities/Trait.kt index f03c2c8..ca8579f 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/entities/Trait.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/entities/Trait.kt @@ -3,23 +3,25 @@ package com.flagsmith.entities import com.google.gson.annotations.SerializedName + data class Trait ( val identifier: String? = null, @SerializedName(value = "trait_key") val key: String, - @SerializedName(value = "trait_value") val traitValue: Any + @SerializedName(value = "trait_value") val traitValue: Any, + val transient: Boolean? = null ) { - constructor(key: String, value: String) - : this(key = key, traitValue = value) + constructor(key: String, value: String, transient: Boolean? = null) + : this(key = key, traitValue = value, transient = transient) - constructor(key: String, value: Int) - : this(key = key, traitValue = value) + constructor(key: String, value: Int, transient: Boolean? = null) + : this(key = key, traitValue = value, transient = transient) - constructor(key: String, value: Double) - : this(key = key, traitValue = value) + constructor(key: String, value: Double, transient: Boolean? = null) + : this(key = key, traitValue = value, transient = transient) - constructor(key: String, value: Boolean) - : this(key = key, traitValue = value) + constructor(key: String, value: Boolean, transient: Boolean? = null) + : this(key = key, traitValue = value, transient = transient) @Deprecated("Use traitValue instead or one of the type-safe getters", ReplaceWith("traitValue")) val value: String @@ -42,25 +44,25 @@ data class Trait ( val booleanValue: Boolean? get() = traitValue as? Boolean - } data class TraitWithIdentity ( @SerializedName(value = "trait_key") val key: String, @SerializedName(value = "trait_value") val traitValue: Any, val identity: Identity, + val transient: Boolean? = null ) { - constructor(key: String, value: String, identity: Identity) - : this(key = key, traitValue = value, identity = identity) + constructor(key: String, value: String, identity: Identity, transient: Boolean? = null) + : this(key = key, traitValue = value, identity = identity, transient = transient) - constructor(key: String, value: Int, identity: Identity) - : this(key = key, traitValue = value, identity = identity) + constructor(key: String, value: Int, identity: Identity, transient: Boolean? = null) + : this(key = key, traitValue = value, identity = identity, transient = transient) - constructor(key: String, value: Double, identity: Identity) - : this(key = key, traitValue = value, identity = identity) + constructor(key: String, value: Double, identity: Identity, transient: Boolean? = null) + : this(key = key, traitValue = value, identity = identity, transient = transient) - constructor(key: String, value: Boolean, identity: Identity) - : this(key = key, traitValue = value, identity = identity) + constructor(key: String, value: Boolean, identity: Identity, transient: Boolean? = null) + : this(key = key, traitValue = value, identity = identity, transient = transient) @Deprecated("Use traitValue instead or one of the type-safe getters", ReplaceWith("traitValue")) val value: String diff --git a/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithRetrofitService.kt b/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithRetrofitService.kt index 9337d73..ba7b5c1 100644 --- a/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithRetrofitService.kt +++ b/FlagsmithClient/src/main/java/com/flagsmith/internal/FlagsmithRetrofitService.kt @@ -19,7 +19,7 @@ import retrofit2.http.Query interface FlagsmithRetrofitService { @GET("identities/") - fun getIdentityFlagsAndTraits(@Query("identifier") identity: String) : Call + fun getIdentityFlagsAndTraits(@Query("identifier") identity: String, @Query("transient") transient: Boolean? = null) : Call @GET("flags/") fun getFlags() : Call> diff --git a/FlagsmithClient/src/test/java/com/flagsmith/SynchronousFlagsmith.kt b/FlagsmithClient/src/test/java/com/flagsmith/SynchronousFlagsmith.kt index 06c024d..b9f0d73 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/SynchronousFlagsmith.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/SynchronousFlagsmith.kt @@ -25,9 +25,9 @@ suspend fun Flagsmith.getTraitSync(id: String, identity: String): Result suspend fun Flagsmith.setTraitSync(trait: Trait, identity: String) : Result = suspendCoroutine { cont -> this.setTrait(trait, identity) { cont.resume(it) } } -suspend fun Flagsmith.setTraitsSync(traits: List, identity: String) : Result> - = suspendCoroutine { cont -> this.setTraits(traits, identity) { cont.resume(it) } } +suspend fun Flagsmith.setTraitsSync(traits: List, identity: String, transient: Boolean = false) : Result> + = suspendCoroutine { cont -> this.setTraits(traits, identity, transient) { cont.resume(it) } } -suspend fun Flagsmith.getIdentitySync(identity: String): Result - = suspendCoroutine { cont -> this.getIdentity(identity) { cont.resume(it) } } +suspend fun Flagsmith.getIdentitySync(identity: String, transient: Boolean = false): Result + = suspendCoroutine { cont -> this.getIdentity(identity, transient) { cont.resume(it) } } diff --git a/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt b/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt index 962c0c8..a81dac0 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/TraitsTests.kt @@ -104,6 +104,32 @@ class TraitsTests { } } + @Test + fun testSetTraitsWithTransient() { + mockServer.mockResponseFor(MockEndpoint.SET_TRANSIENT_TRAITS) + runBlocking { + val result = + flagsmith.setTraitsSync( + listOf( + Trait( + key = "trait-one-with-transient", + value = "transient-trait-one", + transient = true + ), + Trait( + key = "trait-two", + value = "trait-two-value", + transient = false + ), + ), "identity-with-transient-traits") + assertTrue(result.isSuccess) + assertEquals("trait-one-with-transient", result.getOrThrow().first().key) + assertEquals("transient-trait-one", result.getOrThrow().first().stringValue) + assertEquals("identity-with-transient-traits", result.getOrThrow().first().identity.identifier) + assertEquals(true, result.getOrThrow().first().transient) + } + } + @Test fun testSetTraitInteger() { mockServer.mockResponseFor(MockEndpoint.SET_TRAIT_INTEGER) @@ -122,7 +148,7 @@ class TraitsTests { mockServer.mockResponseFor(MockEndpoint.SET_TRAIT_DOUBLE) runBlocking { val result = - flagsmith.setTraitSync(Trait(key = "set-from-client", value = 0.5), "person") + flagsmith.setTraitSync(Trait(key = "set-from-client", traitValue = 0.5), "person") assertTrue(result.isSuccess) assertEquals("set-from-client", result.getOrThrow().key) assertEquals(0.5, result.getOrThrow().doubleValue) @@ -135,7 +161,7 @@ class TraitsTests { mockServer.mockResponseFor(MockEndpoint.SET_TRAIT_BOOLEAN) runBlocking { val result = - flagsmith.setTraitSync(Trait(key = "set-from-client", value = true), "person") + flagsmith.setTraitSync(Trait(key = "set-from-client", traitValue = true), "person") assertTrue(result.isSuccess) assertEquals("set-from-client", result.getOrThrow().key) assertEquals(true, result.getOrThrow().booleanValue) @@ -157,4 +183,20 @@ class TraitsTests { ) } } -} \ No newline at end of file + + @Test + fun testGetTransientIdentity() { + mockServer.mockResponseFor(MockEndpoint.GET_TRANSIENT_IDENTITIES) + runBlocking { + val result = flagsmith.getIdentitySync("transient-identity", true) + assertTrue(result.isSuccess) + assertTrue(result.getOrThrow().traits.isNotEmpty()) + assertTrue(result.getOrThrow().flags.isNotEmpty()) + assertEquals( + "electric pink", + result.getOrThrow().traits.find { trait -> trait.key == "favourite-colour" }?.stringValue + ) + assertEquals(true, result.getOrThrow().flags.isNotEmpty()) + } + } +} diff --git a/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/MockResponses.kt b/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/MockResponses.kt index 9dc70e7..faf6e26 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/MockResponses.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/MockResponses.kt @@ -18,6 +18,8 @@ enum class MockEndpoint(val path: String, val body: String) { GET_FLAGS(FlagsEndpoint.path, MockResponses.getFlags), SET_TRAIT(TraitsEndpoint(Trait(key = "", traitValue = ""), "").path, MockResponses.setTrait), SET_TRAITS(TraitsBulkEndpoint(listOf(Trait(key = "", traitValue = "")), "").path, MockResponses.setTraits), + SET_TRANSIENT_TRAITS(TraitsBulkEndpoint(listOf(Trait(key = "", traitValue = "")), "").path, MockResponses.setTransientTraits), + GET_TRANSIENT_IDENTITIES(IdentityFlagsAndTraitsEndpoint("").path, MockResponses.getTransientIdentities), SET_TRAIT_INTEGER(TraitsEndpoint(Trait(key = "", traitValue = ""), "").path, MockResponses.setTraitInteger), SET_TRAIT_DOUBLE(TraitsEndpoint(Trait(key = "", traitValue = ""), "").path, MockResponses.setTraitDouble), SET_TRAIT_BOOLEAN(TraitsEndpoint(Trait(key = "", traitValue = ""), "").path, MockResponses.setTraitBoolean), @@ -126,6 +128,35 @@ object MockResponses { } """.trimIndent() + val getTransientIdentities = """ + { + "flags": [ + { + "feature_state_value": null, + "feature": { + "type": "STANDARD", + "name": "no-value", + "id": 35506 + }, + "enabled": true + } + ], + "traits": [ + { + "trait_value": "12345", + "trait_key": "set-from-client", + "transient": false + }, + { + "trait_value": "electric pink", + "trait_key": "favourite-colour", + "transient": true + } + ], + "transient": true + } + """.trimIndent() + val getFlags = """ [ { @@ -171,6 +202,25 @@ object MockResponses { } """.trimIndent() + val setTransientTraits = """ + { + "identifier": "identity-with-transient-traits", + "flags": [], + "traits": [ + { + "trait_value": "transient-trait-one", + "trait_key": "trait-one-with-transient", + "transient": true + }, + { + "trait_value": "transient-trait-two", + "trait_key": "trait-two-with-transient", + "transient": false + } + ] + } + """.trimIndent() + val setTraits = """ { "identifier": "person", diff --git a/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/endpoints/IdentityFlagsAndTraitsEndpoint.kt b/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/endpoints/IdentityFlagsAndTraitsEndpoint.kt index ce62a5c..d5372f1 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/endpoints/IdentityFlagsAndTraitsEndpoint.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/endpoints/IdentityFlagsAndTraitsEndpoint.kt @@ -2,8 +2,8 @@ package com.flagsmith.mockResponses.endpoints import com.flagsmith.entities.IdentityFlagsAndTraits -data class IdentityFlagsAndTraitsEndpoint(private val identity: String) : +data class IdentityFlagsAndTraitsEndpoint(private val identity: String, private val transient: Boolean = false) : GetEndpoint( path = "/identities/", - params = listOf("identifier" to identity), + params = listOf("identifier" to identity, "transient" to transient), ) \ No newline at end of file diff --git a/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/endpoints/TraitsEndpoint.kt b/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/endpoints/TraitsEndpoint.kt index ab371b3..fbab66f 100644 --- a/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/endpoints/TraitsEndpoint.kt +++ b/FlagsmithClient/src/test/java/com/flagsmith/mockResponses/endpoints/TraitsEndpoint.kt @@ -5,13 +5,14 @@ import com.flagsmith.entities.Trait import com.flagsmith.entities.TraitWithIdentity import com.google.gson.Gson -data class TraitsEndpoint(private val trait: Trait, private val identity: String) : +data class TraitsEndpoint(private val trait: Trait, private val identity: String, private val transient: Boolean? = null) : PostEndpoint( path = "/identities/", body = Gson().toJson( IdentityAndTraits( identifier = identity, - traits = listOf(trait) + traits = listOf(trait), + transient = transient, ) ), ) \ No newline at end of file