Skip to content

use /flags instead of /decide #245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## Next

- use /flags instead of /decide ([#245](https://github.com/PostHog/posthog-android/pull/245))

## 3.17.0 - 2025-06-05

- chore: do not capture screenshot during screen changes ([#254](https://github.com/PostHog/posthog-android/pull/254))
Expand Down
8 changes: 4 additions & 4 deletions posthog-android/consumer-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
-keep class com.posthog.internal.PostHogBatchEvent { *; }
-keep class com.posthog.internal.PostHogBatchEvent { <init>(); }

-keep class com.posthog.internal.PostHogDecideRequest { *; }
-keep class com.posthog.internal.PostHogDecideRequest { <init>(); }
-keep class com.posthog.internal.PostHogFlagsRequest { *; }
-keep class com.posthog.internal.PostHogFlagsRequest { <init>(); }

-keep class com.posthog.internal.PostHogDecideResponse { *; }
-keep class com.posthog.internal.PostHogDecideResponse { <init>(); }
-keep class com.posthog.internal.PostHogFlagsResponse { *; }
-keep class com.posthog.internal.PostHogFlagsResponse { <init>(); }

-keep class com.posthog.internal.FeatureFlag { *; }
-keep class com.posthog.internal.FeatureFlag { <init>(); }
Expand Down
4 changes: 2 additions & 2 deletions posthog/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ configure<SourceSetContainer> {
}

animalsniffer {
// com.posthog.internal.PostHogDecideRequest Undefined reference (android-api-level-21-5.0.1_r2): boolean java.util.HashMap.remove(Object, Object)
// com.posthog.internal.PostHogDecideRequest Undefined reference (android-api-level-21-5.0.1_r2): Object java.util.HashMap.getOrDefault(Object, Object)
// com.posthog.internal.PostHogFlagsRequest Undefined reference (android-api-level-21-5.0.1_r2): boolean java.util.HashMap.remove(Object, Object)
// com.posthog.internal.PostHogFlagsRequest Undefined reference (android-api-level-21-5.0.1_r2): Object java.util.HashMap.getOrDefault(Object, Object)
// we don't use these methods, so ignore them, they are only available on Android >= 24
// [Undefined reference | android-api-level-21-5.0.1_r2] com.posthog.internal.(RREvent.kt:1)
// >> int Integer.hashCode(int)
Expand Down
2 changes: 1 addition & 1 deletion posthog/src/main/java/com/posthog/PostHog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ public class PostHog private constructor(
)

if (config?.reuseAnonymousId != true) {
// We keep the AnonymousId to be used by decide calls and identify to link the previousId
// We keep the AnonymousId to be used by flags calls and identify to link the previousId
if (previousDistinctId.isNotBlank()) {
this.anonymousId = previousDistinctId
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.posthog.internal
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

/**
* The reason the feature flag was evaluated as returned from the decide v4 API
* The reason the feature flag was evaluated as returned from the flags v2 API
* @property code the code of the reason
* @property description the description of the reason
* @property condition_index the condition index of the reason
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.posthog.internal
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

/**
* The feature flag data structure for calling the decide API v4. This is what each flag in "flags" will be.
* The feature flag data structure for calling the /flags API v2. This is what each flag in "flags" will be.
* @property key the key of the feature flag
* @property enabled whether the feature flag is enabled
* @property variant the variant of the feature flag
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.posthog.internal
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

/**
* The metadata for a feature flag returned from the decide v4 API
* The metadata for a feature flag returned from the /flags v2 API
* @property id the id of the feature flag
* @property payload the payload of the feature flag
* @property version the version of the feature flag
Expand Down
12 changes: 6 additions & 6 deletions posthog/src/main/java/com/posthog/internal/PostHogApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,19 +112,19 @@ internal class PostHogApi(
}

@Throws(PostHogApiError::class, IOException::class)
fun decide(
fun flags(
distinctId: String,
anonymousId: String?,
groups: Map<String, String>?,
): PostHogDecideResponse? {
val decideRequest = PostHogDecideRequest(config.apiKey, distinctId, anonymousId = anonymousId, groups)
): PostHogFlagsResponse? {
val flagsRequest = PostHogFlagsRequest(config.apiKey, distinctId, anonymousId = anonymousId, groups)

val url = "$theHost/decide/?v=4"
logRequest(decideRequest, url)
val url = "$theHost/flags/?v=2&config=true"
logRequest(flagsRequest, url)

val request =
makeRequest(url) {
config.serializer.serialize(decideRequest, it.bufferedWriter())
config.serializer.serialize(flagsRequest, it.bufferedWriter())
}

client.newCall(request).execute().use {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.posthog.internal

/**
* The decide data structure for calling the decide API
* The data structure for calling the flags API
*/
internal class PostHogDecideRequest(
internal class PostHogFlagsRequest(
apiKey: String,
distinctId: String,
anonymousId: String?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ package com.posthog.internal
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

/**
* The decide response data structure for calling the decide API
* The response data structure for calling the /flags API
* @property errorsWhileComputingFlags if there were errors computing feature flags
* @property featureFlags the feature flags
* @property featureFlagPayloads the feature flag payloads
* @property flags the decide v4 flags.
* @property flags the feature flags.
* @property quotaLimited array of quota limited features
*/
@IgnoreJRERequirement
internal data class PostHogDecideResponse(
internal data class PostHogFlagsResponse(
// assuming theres no errors if not present
val errorsWhileComputingFlags: Boolean = false,
val featureFlags: Map<String, Any>?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal class PostHogRemoteConfig(
private var featureFlags: Map<String, Any>? = null
private var featureFlagPayloads: Map<String, Any?>? = null

// Decide v4 flags. These will later supersede featureFlags and featureFlagPayloads
// /flags v2 flags. These will later supersede featureFlags and featureFlagPayloads
// But for now, we need to support both for back compatibility
private var flags: Map<String, Any>? = null
private var requestId: String? = null
Expand Down Expand Up @@ -122,7 +122,7 @@ internal class PostHogRemoteConfig(
if (hasFlags) {
if (config.preloadFeatureFlags) {
if (distinctId.isNotBlank()) {
// do not process session replay from decide API
// do not process session replay from flags API
// since its already cached via the remote config API
executeFeatureFlags(distinctId, anonymousId, groups, onFeatureFlags, calledFromRemoteConfig = true)
} else {
Expand Down Expand Up @@ -216,7 +216,7 @@ internal class PostHogRemoteConfig(
}

try {
val response = api.decide(distinctId, anonymousId = anonymousId, groups)
val response = api.flags(distinctId, anonymousId = anonymousId, groups)

response?.let {
synchronized(featureFlagsLock) {
Expand All @@ -229,7 +229,7 @@ internal class PostHogRemoteConfig(
return@let
}

val normalizedResponse = normalizeDecideResponse(it)
val normalizedResponse = normalizeFlagsResponse(it)

if (normalizedResponse.errorsWhileComputingFlags) {
// if not all flags were computed, we upsert flags instead of replacing them
Expand All @@ -249,7 +249,7 @@ internal class PostHogRemoteConfig(
this.featureFlagPayloads = normalizedPayloads
}

// only process and cache session replay config from decide API
// only process and cache session replay config from flags API
// if not yet done by the remote config API
if (!calledFromRemoteConfig) {
processSessionRecordingConfig(it.sessionRecording)
Expand Down Expand Up @@ -369,13 +369,13 @@ internal class PostHogRemoteConfig(
return parsedPayloads
}

private fun normalizeDecideResponse(decideResponse: PostHogDecideResponse): PostHogDecideResponse {
val flags = decideResponse.flags
private fun normalizeFlagsResponse(flagsResponse: PostHogFlagsResponse): PostHogFlagsResponse {
val flags = flagsResponse.flags
if (flags != null) {
// This is a v4 response. This means that `featureFlags` and `featureFlagPayloads`
// are not populated. We need to populate them with the values from the flags property.
val newResponse =
decideResponse.copy(
flagsResponse.copy(
featureFlags = flags.mapValues { (_, value) -> value.variant ?: value.enabled },
featureFlagPayloads = flags.mapValues { (_, value) -> value.metadata.payload },
)
Expand All @@ -388,7 +388,7 @@ internal class PostHogRemoteConfig(
}
return newResponse
}
return decideResponse
return flagsResponse
}

private fun loadFeatureFlagsFromCacheIfNeeded() {
Expand Down
46 changes: 23 additions & 23 deletions posthog/src/test/java/com/posthog/PostHogTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ internal class PostHogTest {
private val serializer = PostHogSerializer(PostHogConfig(API_KEY))
private lateinit var config: PostHogConfig

private val file = File("src/test/resources/json/decide-v3/basic-decide-no-errors.json")
private val responseDecideApi = file.readText()
private val file = File("src/test/resources/json/flags-v1/basic-flags-no-errors.json")
private val responseFlagsApi = file.readText()

fun getSut(
host: String,
Expand Down Expand Up @@ -170,7 +170,7 @@ internal class PostHogTest {

val request = http.takeRequest()
assertEquals(1, http.requestCount)
assertEquals("/decide/?v=4", request.path)
assertEquals("/flags/?v=2&config=true", request.path)

sut.close()
}
Expand Down Expand Up @@ -224,7 +224,7 @@ internal class PostHogTest {
)
http.enqueue(
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
val url = http.url("/")

Expand All @@ -237,8 +237,8 @@ internal class PostHogTest {
assertEquals(2, http.requestCount)
assertEquals("/array/${API_KEY}/config", remoteConfigRequest.path)

val decideApiRequest = http.takeRequest()
assertEquals("/decide/?v=4", decideApiRequest.path)
val flagsApiRequest = http.takeRequest()
assertEquals("/flags/?v=2&config=true", flagsApiRequest.path)

sut.close()
}
Expand All @@ -256,7 +256,7 @@ internal class PostHogTest {
)
http.enqueue(
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
val url = http.url("/")

Expand All @@ -278,7 +278,7 @@ internal class PostHogTest {
mockHttp(
response =
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
val url = http.url("/")

Expand All @@ -299,7 +299,7 @@ internal class PostHogTest {
mockHttp(
response =
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
val url = http.url("/")

Expand All @@ -316,14 +316,14 @@ internal class PostHogTest {

@Test
fun `getFeatureFlag captures feature flag event if enabled`() {
val file = File("src/test/resources/json/basic-decide-with-non-active-flags.json")
val responseDecideApi = file.readText()
val file = File("src/test/resources/json/basic-flags-with-non-active-flags.json")
val responseFlagsApi = file.readText()

val http =
mockHttp(
response =
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
http.enqueue(
MockResponse()
Expand Down Expand Up @@ -378,14 +378,14 @@ internal class PostHogTest {

@Test
fun `isFeatureEnabled captures feature flag event if enabled`() {
val file = File("src/test/resources/json/basic-decide-with-non-active-flags.json")
val responseDecideApi = file.readText()
val file = File("src/test/resources/json/basic-flags-with-non-active-flags.json")
val responseFlagsApi = file.readText()

val http =
mockHttp(
response =
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
http.enqueue(
MockResponse()
Expand Down Expand Up @@ -430,14 +430,14 @@ internal class PostHogTest {

@Test
fun `isFeatureEnabled captures feature flag variant response if enabled`() {
val file = File("src/test/resources/json/basic-decide-with-non-active-flags.json")
val responseDecideApi = file.readText()
val file = File("src/test/resources/json/basic-flags-with-non-active-flags.json")
val responseFlagsApi = file.readText()

val http =
mockHttp(
response =
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
http.enqueue(
MockResponse()
Expand Down Expand Up @@ -486,7 +486,7 @@ internal class PostHogTest {
mockHttp(
response =
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
val url = http.url("/")

Expand Down Expand Up @@ -1231,14 +1231,14 @@ internal class PostHogTest {

@Test
fun `do not send feature flags called event twice`() {
val file = File("src/test/resources/json/basic-decide-no-errors.json")
val responseDecideApi = file.readText()
val file = File("src/test/resources/json/basic-flags-no-errors.json")
val responseFlagsApi = file.readText()

val http =
mockHttp(
response =
MockResponse()
.setBody(responseDecideApi),
.setBody(responseFlagsApi),
)
http.enqueue(
MockResponse()
Expand Down Expand Up @@ -1433,7 +1433,7 @@ internal class PostHogTest {

val request = http.takeRequest()
assertEquals(1, http.requestCount)
assertEquals("/decide/?v=4", request.path)
assertEquals("/flags/?v=2&config=true", request.path)

sut.close()
}
Expand Down
Loading
Loading