From 4e407348a509d31a82ec8380be686e3973936593 Mon Sep 17 00:00:00 2001 From: dylan Date: Fri, 21 Feb 2025 16:08:00 -0800 Subject: [PATCH 1/9] added a test too --- .../posthog/internal/PostHogDecideResponse.kt | 2 + .../posthog/internal/PostHogFeatureFlags.kt | 11 ++++++ .../internal/PostHogFeatureFlagsTest.kt | 39 +++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/posthog/src/main/java/com/posthog/internal/PostHogDecideResponse.kt b/posthog/src/main/java/com/posthog/internal/PostHogDecideResponse.kt index c664981b..083afd10 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogDecideResponse.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogDecideResponse.kt @@ -7,6 +7,7 @@ import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement * @property errorsWhileComputingFlags if there were errors computing feature flags * @property featureFlags the feature flags * @property featureFlagPayloads the feature flag payloads + * @property quotaLimited array of quota limited features */ @IgnoreJRERequirement internal data class PostHogDecideResponse( @@ -16,4 +17,5 @@ internal data class PostHogDecideResponse( val featureFlagPayloads: Map?, // its either a boolean or a map, see https://github.com/PostHog/posthog-js/blob/10fd7f4fa083f997d31a4a4c7be7d311d0a95e74/src/types.ts#L235-L243 val sessionRecording: Any? = false, + val quotaLimited: Array? = null, ) diff --git a/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt b/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt index b7022fdf..9b79d0e4 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt @@ -89,6 +89,17 @@ internal class PostHogFeatureFlags( response?.let { synchronized(featureFlagsLock) { + if (response.quotaLimited?.contains("feature_flags") == true) { + config.logger.log("Feature flags are quota limited, clearing existing flags") + this.featureFlags = null + this.featureFlagPayloads = null + config.cachePreferences?.let { preferences -> + preferences.remove(FEATURE_FLAGS) + preferences.remove(FEATURE_FLAGS_PAYLOAD) + } + return@executeSafely + } + if (response.errorsWhileComputingFlags) { // if not all flags were computed, we upsert flags instead of replacing them this.featureFlags = diff --git a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt index 12e5341f..db7c6a85 100644 --- a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt +++ b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt @@ -434,4 +434,43 @@ internal class PostHogFeatureFlagsTest { sut.clear() } + + @Test + fun `clear flags when quota limited`() { + // First load some feature flags + val http = mockHttp( + response = MockResponse().setBody(responseDecideApi), + ) + val url = http.url("/") + val sut = getSut(host = url.toString()) + + sut.loadFeatureFlags("my_identify", anonymousId = "anonId", emptyMap(), null) + executor.awaitExecution() + + // Verify flags are loaded + assertTrue(sut.getFeatureFlag("4535-funnel-bar-viz", defaultValue = false) as Boolean) + assertNotNull(preferences.getValue(FEATURE_FLAGS)) + assertNotNull(preferences.getValue(FEATURE_FLAGS_PAYLOAD)) + + // Now send a quota limited response + val quotaLimitedResponse = """ + { + "featureFlags": {}, + "featureFlagPayloads": {}, + "quotaLimited": ["feature_flags"] + } + """.trimIndent() + + http.enqueue(MockResponse().setBody(quotaLimitedResponse)) + + // Reload flags + sut.loadFeatureFlags("my_identify", anonymousId = "anonId", emptyMap(), null) + + executor.shutdownAndAwaitTermination() + + // Verify flags are cleared + assertNull(sut.getFeatureFlags()) + assertNull(preferences.getValue(FEATURE_FLAGS)) + assertNull(preferences.getValue(FEATURE_FLAGS_PAYLOAD)) + } } From 3673ff5980957aaf887687ce80a6e2272d480b65 Mon Sep 17 00:00:00 2001 From: dylan Date: Fri, 21 Feb 2025 16:44:20 -0800 Subject: [PATCH 2/9] formatting --- .../posthog/internal/PostHogFeatureFlagsTest.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt index db7c6a85..59dc4095 100644 --- a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt +++ b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt @@ -438,9 +438,10 @@ internal class PostHogFeatureFlagsTest { @Test fun `clear flags when quota limited`() { // First load some feature flags - val http = mockHttp( - response = MockResponse().setBody(responseDecideApi), - ) + val http = + mockHttp( + response = MockResponse().setBody(responseDecideApi), + ) val url = http.url("/") val sut = getSut(host = url.toString()) @@ -453,19 +454,20 @@ internal class PostHogFeatureFlagsTest { assertNotNull(preferences.getValue(FEATURE_FLAGS_PAYLOAD)) // Now send a quota limited response - val quotaLimitedResponse = """ + val quotaLimitedResponse = + """ { "featureFlags": {}, "featureFlagPayloads": {}, "quotaLimited": ["feature_flags"] } - """.trimIndent() - + """.trimIndent() + http.enqueue(MockResponse().setBody(quotaLimitedResponse)) // Reload flags sut.loadFeatureFlags("my_identify", anonymousId = "anonId", emptyMap(), null) - + executor.shutdownAndAwaitTermination() // Verify flags are cleared From 61358a61413dcf17f16753268c8eeff21848b492 Mon Sep 17 00:00:00 2001 From: dylan Date: Fri, 21 Feb 2025 16:52:49 -0800 Subject: [PATCH 3/9] more formatting and also simpler test data --- .../internal/PostHogFeatureFlagsTest.kt | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt index 59dc4095..7f33791f 100644 --- a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt +++ b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt @@ -437,42 +437,46 @@ internal class PostHogFeatureFlagsTest { @Test fun `clear flags when quota limited`() { - // First load some feature flags val http = mockHttp( - response = MockResponse().setBody(responseDecideApi), + response = + MockResponse().setBody( + """ + { + "featureFlags": {"flag1": true}, + "featureFlagPayloads": {"flag1": "payload1"} + } + """.trimIndent(), + ), ) val url = http.url("/") val sut = getSut(host = url.toString()) - sut.loadFeatureFlags("my_identify", anonymousId = "anonId", emptyMap(), null) + // Load initial flags + sut.loadFeatureFlags("test_id", null, null, null) executor.awaitExecution() // Verify flags are loaded - assertTrue(sut.getFeatureFlag("4535-funnel-bar-viz", defaultValue = false) as Boolean) + assertNotNull(sut.getFeatureFlags()) assertNotNull(preferences.getValue(FEATURE_FLAGS)) - assertNotNull(preferences.getValue(FEATURE_FLAGS_PAYLOAD)) - - // Now send a quota limited response - val quotaLimitedResponse = - """ - { - "featureFlags": {}, - "featureFlagPayloads": {}, - "quotaLimited": ["feature_flags"] - } - """.trimIndent() - http.enqueue(MockResponse().setBody(quotaLimitedResponse)) + // Send quota limited response + http.enqueue( + MockResponse().setBody( + """ + { + "quotaLimited": ["feature_flags"] + } + """.trimIndent(), + ), + ) // Reload flags - sut.loadFeatureFlags("my_identify", anonymousId = "anonId", emptyMap(), null) - - executor.shutdownAndAwaitTermination() + sut.loadFeatureFlags("test_id", null, null, null) + executor.awaitExecution() // Verify flags are cleared assertNull(sut.getFeatureFlags()) assertNull(preferences.getValue(FEATURE_FLAGS)) - assertNull(preferences.getValue(FEATURE_FLAGS_PAYLOAD)) } } From 21991d0f99a23515162896af5ce8a99e898f962c Mon Sep 17 00:00:00 2001 From: dylan Date: Fri, 21 Feb 2025 16:55:12 -0800 Subject: [PATCH 4/9] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47bbfcb4..afc12137 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Next +## 3.11.3 - 2025-02-21 + +- feat: support quota limiting for feature flags ([#228](https://github.com/PostHog/posthog-android/pull/228)) + ## 3.11.2 - 2025-02-04 - fix: touches fall back to single touches if out of bounds ([#221](https://github.com/PostHog/posthog-android/pull/221)) From 9813f991152d0dfa81cf53981d425e9d02394d57 Mon Sep 17 00:00:00 2001 From: dylan Date: Sun, 23 Feb 2025 13:16:05 -0800 Subject: [PATCH 5/9] code review --- CHANGELOG.md | 2 +- .../src/main/java/com/posthog/internal/PostHogDecideResponse.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afc12137..dacb6f93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Next -## 3.11.3 - 2025-02-21 +## 3.11.3 - 2025-02-24 - feat: support quota limiting for feature flags ([#228](https://github.com/PostHog/posthog-android/pull/228)) diff --git a/posthog/src/main/java/com/posthog/internal/PostHogDecideResponse.kt b/posthog/src/main/java/com/posthog/internal/PostHogDecideResponse.kt index 083afd10..83a770e2 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogDecideResponse.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogDecideResponse.kt @@ -17,5 +17,5 @@ internal data class PostHogDecideResponse( val featureFlagPayloads: Map?, // its either a boolean or a map, see https://github.com/PostHog/posthog-js/blob/10fd7f4fa083f997d31a4a4c7be7d311d0a95e74/src/types.ts#L235-L243 val sessionRecording: Any? = false, - val quotaLimited: Array? = null, + val quotaLimited: List? = null, ) From 2b665137481eaaa4536e9b5bb227bacb682492c6 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Mon, 24 Feb 2025 09:31:33 +0100 Subject: [PATCH 6/9] fix recursion --- .../src/main/java/com/posthog/internal/PostHogFeatureFlags.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt b/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt index 9b79d0e4..2e62f2df 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt @@ -97,7 +97,7 @@ internal class PostHogFeatureFlags( preferences.remove(FEATURE_FLAGS) preferences.remove(FEATURE_FLAGS_PAYLOAD) } - return@executeSafely + return@synchronized } if (response.errorsWhileComputingFlags) { From 62fec9f20aab6606e56772d544842f800a413394 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Mon, 24 Feb 2025 09:42:55 +0100 Subject: [PATCH 7/9] fix --- .../src/main/java/com/posthog/internal/PostHogFeatureFlags.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt b/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt index 2e62f2df..20dfe83a 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogFeatureFlags.kt @@ -97,7 +97,7 @@ internal class PostHogFeatureFlags( preferences.remove(FEATURE_FLAGS) preferences.remove(FEATURE_FLAGS_PAYLOAD) } - return@synchronized + return@let } if (response.errorsWhileComputingFlags) { From 1493a405162fc59281dca381826a3554ddb8f958 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto Date: Mon, 24 Feb 2025 09:48:45 +0100 Subject: [PATCH 8/9] fix lint --- posthog-android/lint-baseline.xml | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/posthog-android/lint-baseline.xml b/posthog-android/lint-baseline.xml index 3b6ffb53..1abed766 100644 --- a/posthog-android/lint-baseline.xml +++ b/posthog-android/lint-baseline.xml @@ -36,7 +36,7 @@ - - - - From d15a895a3caca623ac947b684d8014f71548b585 Mon Sep 17 00:00:00 2001 From: Dylan Martin Date: Tue, 25 Feb 2025 12:37:22 -0500 Subject: [PATCH 9/9] Update CHANGELOG.md Co-authored-by: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com> --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dacb6f93..92de99f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Next -## 3.11.3 - 2025-02-24 +## 3.11.3 - 2025-02-25 - feat: support quota limiting for feature flags ([#228](https://github.com/PostHog/posthog-android/pull/228))