From caf64bdbdeba83c86e7162c8e83ddb1763a0674e Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Thu, 22 Oct 2026 11:55:01 -0400 Subject: [PATCH 1/4] chore!: remove deprecated APIs for posthog-android 4.0 Remove the public APIs already marked deprecated: apiKey and the apiKey factories, propertiesSanitizer and PostHogPropertiesSanitizer, evaluationEnvironments, getFeatureFlagPayload, and the replay debouncerDelayMs. remoteConfig moves to its own change because setting it to false still changes setup behavior. Co-Authored-By: Claude Opus 5.5 (1M context) --- .changeset/android-v4-major.md | 10 ++ posthog-android/api/posthog-android.api | 13 +- .../posthog/android/PostHogAndroidConfig.kt | 36 ----- .../replay/PostHogSessionReplayConfig.kt | 17 --- .../android/PostHogAndroidConfigTest.kt | 12 -- posthog/api/posthog.api | 20 +-- posthog/src/main/java/com/posthog/PostHog.kt | 22 --- .../main/java/com/posthog/PostHogConfig.kt | 132 ------------------ .../main/java/com/posthog/PostHogInterface.kt | 16 --- .../com/posthog/PostHogPropertiesSanitizer.kt | 13 -- .../main/java/com/posthog/PostHogStateless.kt | 5 +- .../java/com/posthog/PostHogConfigTest.kt | 11 -- .../com/posthog/PostHogFeatureFlagsTest.kt | 2 - .../java/com/posthog/PostHogStatelessTest.kt | 35 ----- .../src/test/java/com/posthog/PostHogTest.kt | 49 +------ .../java/com/posthog/PostHogFake.kt | 8 -- 16 files changed, 17 insertions(+), 384 deletions(-) create mode 100644 .changeset/android-v4-major.md delete mode 100644 posthog/src/main/java/com/posthog/PostHogPropertiesSanitizer.kt diff --git a/.changeset/android-v4-major.md b/.changeset/android-v4-major.md new file mode 100644 index 000000000..1b6846771 --- /dev/null +++ b/.changeset/android-v4-major.md @@ -0,0 +1,10 @@ +--- +'posthog': major +'posthog-android': major +--- + +- **Breaking:** remove the deprecated `apiKey` property and the `PostHogConfig(apiKey = …)` and `PostHogAndroidConfig(apiKey = …)` factories; use `projectToken` instead +- **Breaking:** remove the deprecated `propertiesSanitizer` config option and the `PostHogPropertiesSanitizer` interface; use `addBeforeSend` instead +- **Breaking:** remove the deprecated `evaluationEnvironments` config option; use `evaluationContexts` instead +- **Breaking:** remove the deprecated `getFeatureFlagPayload()`; use `getFeatureFlagResult(key)?.payload` instead +- **Breaking:** remove the deprecated `PostHogSessionReplayConfig.debouncerDelayMs`; use `throttleDelayMs` instead diff --git a/posthog-android/api/posthog-android.api b/posthog-android/api/posthog-android.api index 71c9f8c4a..ee90e68c6 100644 --- a/posthog-android/api/posthog-android.api +++ b/posthog-android/api/posthog-android.api @@ -17,7 +17,6 @@ public final class com/posthog/android/PostHogAndroid$Companion { } public class com/posthog/android/PostHogAndroidConfig : com/posthog/PostHogConfig { - public static final field Companion Lcom/posthog/android/PostHogAndroidConfig$Companion; public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Ljava/lang/String;)V public fun (Ljava/lang/String;Ljava/lang/String;Z)V @@ -47,11 +46,6 @@ public class com/posthog/android/PostHogAndroidConfig : com/posthog/PostHogConfi public final fun setSessionReplayConfig (Lcom/posthog/android/replay/PostHogSessionReplayConfig;)V } -public final class com/posthog/android/PostHogAndroidConfig$Companion { - public final synthetic fun invoke (Ljava/lang/String;Ljava/lang/String;ZZZLcom/posthog/android/replay/PostHogSessionReplayConfig;ZZ)Lcom/posthog/android/PostHogAndroidConfig; - public static synthetic fun invoke$default (Lcom/posthog/android/PostHogAndroidConfig$Companion;Ljava/lang/String;Ljava/lang/String;ZZZLcom/posthog/android/replay/PostHogSessionReplayConfig;ZZILjava/lang/Object;)Lcom/posthog/android/PostHogAndroidConfig; -} - public final class com/posthog/android/PostHogAutocaptureModifierKt { public static final fun postHogAutocaptureNoCapture (Landroidx/compose/ui/Modifier;Z)Landroidx/compose/ui/Modifier; public static synthetic fun postHogAutocaptureNoCapture$default (Landroidx/compose/ui/Modifier;ZILjava/lang/Object;)Landroidx/compose/ui/Modifier; @@ -130,12 +124,10 @@ public final class com/posthog/android/replay/PostHogSessionReplayConfig { public fun (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;)V public fun (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;Z)V public fun (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJ)V - public fun (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJJ)V - public fun (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJJLjava/lang/Double;)V - public synthetic fun (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJJLjava/lang/Double;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJLjava/lang/Double;)V + public synthetic fun (ZZZLcom/posthog/android/replay/PostHogDrawableConverter;ZJLjava/lang/Double;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getCaptureLogcat ()Z public final fun getCaptureTouches ()Z - public final fun getDebouncerDelayMs ()J public final fun getDrawableConverter ()Lcom/posthog/android/replay/PostHogDrawableConverter; public final fun getMaskAllImages ()Z public final fun getMaskAllTextInputs ()Z @@ -148,7 +140,6 @@ public final class com/posthog/android/replay/PostHogSessionReplayConfig { public final fun getVerifyScreenshotMaskAlignment ()Z public final fun setCaptureLogcat (Z)V public final fun setCaptureTouches (Z)V - public final fun setDebouncerDelayMs (J)V public final fun setDrawableConverter (Lcom/posthog/android/replay/PostHogDrawableConverter;)V public final fun setMaskAllImages (Z)V public final fun setMaskAllTextInputs (Z)V diff --git a/posthog-android/src/main/java/com/posthog/android/PostHogAndroidConfig.kt b/posthog-android/src/main/java/com/posthog/android/PostHogAndroidConfig.kt index 3d188f772..7d406922e 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogAndroidConfig.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogAndroidConfig.kt @@ -96,40 +96,4 @@ public open class PostHogAndroidConfig public var captureDeadClicks: Boolean = false internal var replayQueueHolder: PostHogReplayQueue? = null - - public companion object { - /** - * Deprecated form of the [PostHogAndroidConfig] constructor that takes `apiKey` instead - * of `projectToken`, kept so Kotlin calls such as `PostHogAndroidConfig(apiKey = "phc_...")` - * still compile. Every other parameter and default matches the constructor. - * - * ```kotlin - * val config = PostHogAndroidConfig(projectToken = "phc_your_project_token") // instead of apiKey = - * ``` - */ - @Deprecated( - "Deprecated in favor of projectToken. This will be removed in the next major version.", - ) - @JvmSynthetic - public operator fun invoke( - apiKey: String, - host: String = PostHogConfig.DEFAULT_HOST, - captureApplicationLifecycleEvents: Boolean = true, - captureDeepLinks: Boolean = true, - captureScreenViews: Boolean = true, - sessionReplayConfig: PostHogSessionReplayConfig = PostHogSessionReplayConfig(), - capturePushNotificationSubscriptions: Boolean = true, - capturePushNotificationOpened: Boolean = true, - ): PostHogAndroidConfig = - PostHogAndroidConfig( - projectToken = apiKey, - host = host, - captureApplicationLifecycleEvents = captureApplicationLifecycleEvents, - captureDeepLinks = captureDeepLinks, - captureScreenViews = captureScreenViews, - sessionReplayConfig = sessionReplayConfig, - capturePushNotificationSubscriptions = capturePushNotificationSubscriptions, - capturePushNotificationOpened = capturePushNotificationOpened, - ) - } } diff --git a/posthog-android/src/main/java/com/posthog/android/replay/PostHogSessionReplayConfig.kt b/posthog-android/src/main/java/com/posthog/android/replay/PostHogSessionReplayConfig.kt index bc3c492a8..8590f86ec 100644 --- a/posthog-android/src/main/java/com/posthog/android/replay/PostHogSessionReplayConfig.kt +++ b/posthog-android/src/main/java/com/posthog/android/replay/PostHogSessionReplayConfig.kt @@ -38,15 +38,6 @@ public class PostHogSessionReplayConfig * The screenshot may contain sensitive information, use with caution. */ public var screenshot: Boolean = false, - /** - * Debouncer delay used to reduce the number of snapshots captured and reduce performance impact. - * This is used for capturing the view as a wireframe or screenshot. - * The lower the number, the more snapshots are captured and the higher the performance impact. - * Defaults to 1000ms = 1s. - * Ps: it was 500ms by default until version 3.8.2. - */ - @Deprecated("Use throttleDelayMs instead") - public var debouncerDelayMs: Long = 1000, /** * Throttling delay used to reduce the number of snapshots captured and reduce performance impact. * This is used for capturing the view as a wireframe or screenshot. @@ -118,12 +109,4 @@ public class PostHogSessionReplayConfig @PostHogExperimental @Volatile public var screenshotColorMode: PostHogScreenshotColorMode = PostHogScreenshotColorMode.ARGB_8888 - - init { - // for keeping back compatibility - @Suppress("DEPRECATION") - if (debouncerDelayMs != 1000L) { - throttleDelayMs = debouncerDelayMs - } - } } diff --git a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidConfigTest.kt b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidConfigTest.kt index 23d74cd34..ac6bc81c1 100644 --- a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidConfigTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidConfigTest.kt @@ -21,18 +21,6 @@ internal class PostHogAndroidConfigTest { assertEquals(PROJECT_TOKEN, config.projectToken) } - @Suppress("DEPRECATION") - @Test - fun `deprecated apiKey factory and getter alias projectToken`() { - val config = PostHogAndroidConfig(apiKey = " $PROJECT_TOKEN ", captureDeepLinks = false, capturePushNotificationOpened = false) - - assertEquals(PROJECT_TOKEN, config.projectToken) - assertEquals(PROJECT_TOKEN, config.apiKey) - assertFalse(config.captureDeepLinks) - assertFalse(config.capturePushNotificationOpened) - assertTrue(config.captureScreenViews) - } - @Test fun `captureApplicationLifecycleEvents should be enabled by default`() { assertTrue(config.captureApplicationLifecycleEvents) diff --git a/posthog/api/posthog.api b/posthog/api/posthog.api index 77e664f61..c744204de 100644 --- a/posthog/api/posthog.api +++ b/posthog/api/posthog.api @@ -39,7 +39,6 @@ public final class com/posthog/PostHog : com/posthog/PostHogStateless, com/posth public fun getConfig ()Lcom/posthog/PostHogConfig; public fun getDeviceId ()Ljava/lang/String; public fun getFeatureFlag (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Boolean;)Ljava/lang/Object; - public fun getFeatureFlagPayload (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; public fun getFeatureFlagResult (Ljava/lang/String;Ljava/lang/Boolean;)Lcom/posthog/FeatureFlagResult; public fun getLogger ()Lcom/posthog/logs/PostHogLogger; public fun getSessionId ()Ljava/util/UUID; @@ -88,7 +87,6 @@ public final class com/posthog/PostHog$Companion : com/posthog/PostHogInterface public fun getConfig ()Lcom/posthog/PostHogConfig; public fun getDeviceId ()Ljava/lang/String; public fun getFeatureFlag (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Boolean;)Ljava/lang/Object; - public fun getFeatureFlagPayload (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; public fun getFeatureFlagResult (Ljava/lang/String;Ljava/lang/Boolean;)Lcom/posthog/FeatureFlagResult; public fun getLogger ()Lcom/posthog/logs/PostHogLogger; public fun getSessionId ()Ljava/util/UUID; @@ -158,11 +156,10 @@ public class com/posthog/PostHogConfig { public static final field DEFAULT_MAX_QUEUE_SIZE I public static final field DEFAULT_US_ASSETS_HOST Ljava/lang/String; public static final field DEFAULT_US_HOST Ljava/lang/String; - public fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;IILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addBeforeSend (Lcom/posthog/PostHogBeforeSend;)V public final fun addIntegration (Lcom/posthog/PostHogIntegration;)V - public final fun getApiKey ()Ljava/lang/String; public final fun getBeforeSendList ()Ljava/util/List; public final fun getBootstrap ()Lcom/posthog/PostHogBootstrapConfig; public final fun getCachePreferences ()Lcom/posthog/internal/PostHogPreferences; @@ -173,7 +170,6 @@ public class com/posthog/PostHogConfig { public final fun getEncryption ()Lcom/posthog/PostHogEncryption; public final fun getErrorTrackingConfig ()Lcom/posthog/errortracking/PostHogErrorTrackingConfig; public final fun getEvaluationContexts ()Ljava/util/List; - public final fun getEvaluationEnvironments ()Ljava/util/List; public final fun getFeatureFlagCalledCacheSize ()I public final fun getFeatureFlagRequestMaxRetries ()I public final fun getFlushAt ()I @@ -196,7 +192,6 @@ public class com/posthog/PostHogConfig { public final fun getPersonProfiles ()Lcom/posthog/PersonProfiles; public final fun getPreloadFeatureFlags ()Z public final fun getProjectToken ()Ljava/lang/String; - public final fun getPropertiesSanitizer ()Lcom/posthog/PostHogPropertiesSanitizer; public final fun getProxy ()Ljava/net/Proxy; public final fun getPushIdentityProvider ()Lkotlin/jvm/functions/Function3; public final fun getQueueProvider ()Lkotlin/jvm/functions/Function5; @@ -229,7 +224,6 @@ public class com/posthog/PostHogConfig { public final fun setDebug (Z)V public final fun setEncryption (Lcom/posthog/PostHogEncryption;)V public final fun setEvaluationContexts (Ljava/util/List;)V - public final fun setEvaluationEnvironments (Ljava/util/List;)V public final fun setFeatureFlagCalledCacheSize (I)V public final fun setFeatureFlagRequestMaxRetries (I)V public final fun setFlushAt (I)V @@ -249,7 +243,6 @@ public class com/posthog/PostHogConfig { public final fun setPersistOptOut (Z)V public final fun setPersonProfiles (Lcom/posthog/PersonProfiles;)V public final fun setPreloadFeatureFlags (Z)V - public final fun setPropertiesSanitizer (Lcom/posthog/PostHogPropertiesSanitizer;)V public final fun setProxy (Ljava/net/Proxy;)V public final fun setPushIdentityProvider (Lkotlin/jvm/functions/Function3;)V public final fun setReleaseIdentifier (Ljava/lang/String;)V @@ -272,8 +265,6 @@ public class com/posthog/PostHogConfig { } public final class com/posthog/PostHogConfig$Companion { - public final synthetic fun invoke (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;)Lcom/posthog/PostHogConfig; - public static synthetic fun invoke$default (Lcom/posthog/PostHogConfig$Companion;Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLcom/posthog/PostHogPropertiesSanitizer;Lkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;IILjava/lang/Object;)Lcom/posthog/PostHogConfig; } public abstract interface class com/posthog/PostHogCoreInterface { @@ -395,8 +386,6 @@ public abstract interface class com/posthog/PostHogInterface : com/posthog/PostH public abstract fun getDeviceId ()Ljava/lang/String; public abstract fun getFeatureFlag (Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Boolean;)Ljava/lang/Object; public static synthetic fun getFeatureFlag$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Boolean;ILjava/lang/Object;)Ljava/lang/Object; - public abstract fun getFeatureFlagPayload (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; - public static synthetic fun getFeatureFlagPayload$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object; public abstract fun getFeatureFlagResult (Ljava/lang/String;Ljava/lang/Boolean;)Lcom/posthog/FeatureFlagResult; public static synthetic fun getFeatureFlagResult$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/Boolean;ILjava/lang/Object;)Lcom/posthog/FeatureFlagResult; public abstract fun getLogger ()Lcom/posthog/logs/PostHogLogger; @@ -441,7 +430,6 @@ public final class com/posthog/PostHogInterface$DefaultImpls { public static synthetic fun captureLog$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Lcom/posthog/logs/PostHogLogSeverity;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;ILjava/lang/Object;)V public static synthetic fun capturePushNotificationOpened$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;ILjava/lang/Object;)V public static synthetic fun getFeatureFlag$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Boolean;ILjava/lang/Object;)Ljava/lang/Object; - public static synthetic fun getFeatureFlagPayload$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/Object;ILjava/lang/Object;)Ljava/lang/Object; public static synthetic fun getFeatureFlagResult$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/Boolean;ILjava/lang/Object;)Lcom/posthog/FeatureFlagResult; public static synthetic fun group$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)V public static synthetic fun isFeatureEnabled$default (Lcom/posthog/PostHogInterface;Ljava/lang/String;ZLjava/lang/Boolean;ILjava/lang/Object;)Z @@ -475,10 +463,6 @@ public abstract interface class com/posthog/PostHogOptInReceiver { public abstract fun onOptIn ()V } -public abstract interface class com/posthog/PostHogPropertiesSanitizer { - public abstract fun sanitize (Ljava/util/Map;)Ljava/util/Map; -} - public class com/posthog/PostHogStateless : com/posthog/PostHogStatelessInterface { public static final field Companion Lcom/posthog/PostHogStateless$Companion; protected field config Lcom/posthog/PostHogConfig; diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index 281503ecb..6bd98ff1e 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -2100,18 +2100,6 @@ public class PostHog private constructor( return results } - @Deprecated( - message = "Use getFeatureFlagResult() instead; note it sends the \$feature_flag_called event by default.", - replaceWith = ReplaceWith("getFeatureFlagResult(key)?.payload"), - ) - public override fun getFeatureFlagPayload( - key: String, - defaultValue: Any?, - ): Any? { - if (!isEnabled()) return defaultValue - return getFeatureFlagResult(key, sendFeatureFlagEvent = false)?.payload ?: defaultValue - } - public override fun getFeatureFlagResult( key: String, sendFeatureFlagEvent: Boolean?, @@ -2748,16 +2736,6 @@ public class PostHog private constructor( return shared.getAllFeatureFlags() } - @Deprecated( - message = "Use getFeatureFlagResult() instead; note it sends the \$feature_flag_called event by default.", - replaceWith = ReplaceWith("getFeatureFlagResult(key)?.payload"), - ) - @Suppress("DEPRECATION") - public override fun getFeatureFlagPayload( - key: String, - defaultValue: Any?, - ): Any? = shared.getFeatureFlagPayload(key, defaultValue = defaultValue) - public override fun getFeatureFlagResult( key: String, sendFeatureFlagEvent: Boolean?, diff --git a/posthog/src/main/java/com/posthog/PostHogConfig.kt b/posthog/src/main/java/com/posthog/PostHogConfig.kt index eb730adab..c01078500 100644 --- a/posthog/src/main/java/com/posthog/PostHogConfig.kt +++ b/posthog/src/main/java/com/posthog/PostHogConfig.kt @@ -173,12 +173,6 @@ public open class PostHogConfig( * Defaults to false */ public var sessionReplay: Boolean = false, - /** - * Hook that allows to sanitize the event properties - * The hook is called before the event is cached or sent over the wire - */ - @Deprecated("Use beforeSendList instead") - public var propertiesSanitizer: PostHogPropertiesSanitizer? = null, /** * Hook that allows for modification of the default mechanism for * generating anonymous id (which as of now is just random UUID v7) @@ -446,20 +440,6 @@ public open class PostHogConfig( */ public val projectToken: String = projectToken.trim() - /** - * Deprecated alias for [projectToken]; returns the same trimmed value. - * - * ```kotlin - * val token = config.projectToken // instead of config.apiKey - * ``` - */ - @Deprecated( - "Deprecated in favor of projectToken. This will be removed in the next major version.", - ReplaceWith("projectToken"), - ) - public val apiKey: String - get() = projectToken - /** * The PostHog Host * Defaults to https://us.i.posthog.com @@ -602,20 +582,6 @@ public open class PostHogConfig( } } - /** - * Optional list of evaluation environment tags for feature flag evaluation - * @deprecated Use evaluationContexts instead. This property will be removed in a future version. - */ - @Deprecated( - message = "Use evaluationContexts instead. This property will be removed in a future version.", - replaceWith = ReplaceWith("evaluationContexts"), - ) - public var evaluationEnvironments: List? - get() = evaluationContexts - set(value) { - evaluationContexts = value - } - public companion object { /** Shared default for [flushAt] and [PostHogLogsConfig.flushAt]. */ public const val DEFAULT_FLUSH_AT: Int = 20 @@ -639,104 +605,6 @@ public open class PostHogConfig( public const val DEFAULT_EU_ASSETS_HOST: String = "https://eu-assets.i.posthog.com" public const val DEFAULT_FEATURE_FLAG_CALLED_CACHE_SIZE: Int = 1000 - - /** - * Deprecated form of the [PostHogConfig] constructor that takes `apiKey` instead of - * `projectToken`, kept so Kotlin calls such as `PostHogConfig(apiKey = "phc_...")` still - * compile. Every other parameter and default matches the constructor. - * - * ```kotlin - * val config = PostHogConfig(projectToken = "phc_your_project_token") // instead of apiKey = - * ``` - */ - @Deprecated( - "Deprecated in favor of projectToken. This will be removed in the next major version.", - ) - @Suppress("DEPRECATION") - @JvmSynthetic - public operator fun invoke( - apiKey: String, - host: String = DEFAULT_HOST, - debug: Boolean = false, - optOut: Boolean = false, - sendFeatureFlagEvent: Boolean = true, - featureFlagCalledCacheSize: Int = DEFAULT_FEATURE_FLAG_CALLED_CACHE_SIZE, - preloadFeatureFlags: Boolean = true, - evaluationContexts: List? = null, - setDefaultPersonProperties: Boolean = true, - remoteConfig: Boolean = true, - flushAt: Int = DEFAULT_FLUSH_AT, - maxQueueSize: Int = DEFAULT_MAX_QUEUE_SIZE, - maxBatchSize: Int = DEFAULT_MAX_BATCH_SIZE, - maxRetries: Int = 3, - featureFlagRequestMaxRetries: Int = 1, - flushIntervalSeconds: Int = DEFAULT_FLUSH_INTERVAL_SECONDS, - encryption: PostHogEncryption? = null, - onFeatureFlags: PostHogOnFeatureFlags? = null, - sessionReplay: Boolean = false, - propertiesSanitizer: PostHogPropertiesSanitizer? = null, - getAnonymousId: ((UUID) -> UUID) = { it }, - reuseAnonymousId: Boolean = false, - personProfiles: PersonProfiles = PersonProfiles.IDENTIFIED_ONLY, - surveys: Boolean = false, - proxy: Proxy? = null, - surveysConfig: PostHogSurveysConfig = PostHogSurveysConfig(), - logs: PostHogLogsConfig = PostHogLogsConfig(), - remoteConfigProvider: ( - PostHogConfig, - PostHogApi, - ExecutorService, - PostHogDefaultPersonPropertiesProvider?, - PostHogOnRemoteConfigLoaded?, - PostHogFeatureFlagCalledProvider?, - ) -> PostHogFeatureFlagsInterface = DEFAULT_REMOTE_CONFIG_PROVIDER, - queueProvider: ( - PostHogConfig, - PostHogApi, - PostHogApiEndpoint, - String?, - ExecutorService, - ) -> PostHogQueueInterface = DEFAULT_QUEUE_PROVIDER, - errorTrackingConfig: PostHogErrorTrackingConfig = PostHogErrorTrackingConfig(), - releaseIdentifier: String? = null, - bootstrap: PostHogBootstrapConfig? = null, - pushIdentityProvider: ((distinctId: String, appId: String, completion: (String?) -> Unit) -> Unit)? = null, - ): PostHogConfig = - PostHogConfig( - projectToken = apiKey, - host = host, - debug = debug, - optOut = optOut, - sendFeatureFlagEvent = sendFeatureFlagEvent, - featureFlagCalledCacheSize = featureFlagCalledCacheSize, - preloadFeatureFlags = preloadFeatureFlags, - evaluationContexts = evaluationContexts, - setDefaultPersonProperties = setDefaultPersonProperties, - remoteConfig = remoteConfig, - flushAt = flushAt, - maxQueueSize = maxQueueSize, - maxBatchSize = maxBatchSize, - maxRetries = maxRetries, - featureFlagRequestMaxRetries = featureFlagRequestMaxRetries, - flushIntervalSeconds = flushIntervalSeconds, - encryption = encryption, - onFeatureFlags = onFeatureFlags, - sessionReplay = sessionReplay, - propertiesSanitizer = propertiesSanitizer, - getAnonymousId = getAnonymousId, - reuseAnonymousId = reuseAnonymousId, - personProfiles = personProfiles, - surveys = surveys, - proxy = proxy, - surveysConfig = surveysConfig, - logs = logs, - remoteConfigProvider = remoteConfigProvider, - queueProvider = queueProvider, - errorTrackingConfig = errorTrackingConfig, - releaseIdentifier = releaseIdentifier, - bootstrap = bootstrap, - pushIdentityProvider = pushIdentityProvider, - ) } } diff --git a/posthog/src/main/java/com/posthog/PostHogInterface.kt b/posthog/src/main/java/com/posthog/PostHogInterface.kt index 126d09734..50e76b49b 100644 --- a/posthog/src/main/java/com/posthog/PostHogInterface.kt +++ b/posthog/src/main/java/com/posthog/PostHogInterface.kt @@ -163,22 +163,6 @@ public interface PostHogInterface : PostHogCoreInterface { */ public fun getAllFeatureFlags(): List? - /** - * Returns the feature flag payload - * Docs https://posthog.com/docs/feature-flags and https://posthog.com/docs/experiments - * @param key the Key - * @param defaultValue the default value if not found - * @return The feature flag payload, or [defaultValue] if not found. - */ - @Deprecated( - message = "Use getFeatureFlagResult() instead; note it sends the \$feature_flag_called event by default.", - replaceWith = ReplaceWith("getFeatureFlagResult(key)?.payload"), - ) - public fun getFeatureFlagPayload( - key: String, - defaultValue: Any? = null, - ): Any? - /** * Returns the feature flag result containing both value and payload. * This is the recommended way to access feature flags when you need both diff --git a/posthog/src/main/java/com/posthog/PostHogPropertiesSanitizer.kt b/posthog/src/main/java/com/posthog/PostHogPropertiesSanitizer.kt deleted file mode 100644 index 8d4f0d5f1..000000000 --- a/posthog/src/main/java/com/posthog/PostHogPropertiesSanitizer.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.posthog - -/** - * Hook to sanitize the event properties - */ -public fun interface PostHogPropertiesSanitizer { - /** - * Sanitizes the event properties - * @param properties the event properties to sanitize - * @return the sanitized properties - */ - public fun sanitize(properties: MutableMap): Map -} diff --git a/posthog/src/main/java/com/posthog/PostHogStateless.kt b/posthog/src/main/java/com/posthog/PostHogStateless.kt index bc54145ad..bd6505876 100644 --- a/posthog/src/main/java/com/posthog/PostHogStateless.kt +++ b/posthog/src/main/java/com/posthog/PostHogStateless.kt @@ -292,20 +292,17 @@ public open class PostHogStateless protected constructor( } } - @Suppress("DEPRECATION") protected fun buildEvent( event: String, distinctId: String, properties: MutableMap, timestamp: Date? = null, ): PostHogEvent? { - // sanitize the properties or fallback to the original properties - val sanitizedProperties = config?.propertiesSanitizer?.sanitize(properties)?.toMutableMap() ?: properties val postHogEvent = PostHogEvent( event, distinctId, - properties = sanitizedProperties, + properties = properties, timestamp = timestamp ?: config?.dateProvider?.currentDate() ?: Date(), ) var eventChecked: PostHogEvent = postHogEvent diff --git a/posthog/src/test/java/com/posthog/PostHogConfigTest.kt b/posthog/src/test/java/com/posthog/PostHogConfigTest.kt index e650bb8a6..7903540c3 100644 --- a/posthog/src/test/java/com/posthog/PostHogConfigTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogConfigTest.kt @@ -23,17 +23,6 @@ internal class PostHogConfigTest { assertEquals("https://eu.i.posthog.com/", config.host) } - @Suppress("DEPRECATION") - @Test - fun `deprecated apiKey factory and getter alias projectToken`() { - val config = PostHogConfig(apiKey = " $PROJECT_TOKEN ", host = "https://eu.i.posthog.com", flushAt = 7) - - assertEquals(PROJECT_TOKEN, config.projectToken) - assertEquals(PROJECT_TOKEN, config.apiKey) - assertEquals("https://eu.i.posthog.com", config.host) - assertEquals(7, config.flushAt) - } - @Test fun `defaults a blank host after trimming whitespace`() { val config = PostHogConfig(PROJECT_TOKEN, " \n\t ") diff --git a/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt b/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt index 9019adae7..82575e966 100644 --- a/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt @@ -54,7 +54,6 @@ internal class PostHogFeatureFlagsTest { integration: PostHogIntegration? = null, remoteConfig: Boolean = false, cachePreferences: PostHogMemoryPreferences = PostHogMemoryPreferences(), - propertiesSanitizer: PostHogPropertiesSanitizer? = null, beforeSend: PostHogBeforeSend? = null, evaluationContexts: List? = null, context: PostHogContext? = null, @@ -73,7 +72,6 @@ internal class PostHogFeatureFlagsTest { this.sendFeatureFlagEvent = sendFeatureFlagEvent this.reuseAnonymousId = reuseAnonymousId this.cachePreferences = cachePreferences - this.propertiesSanitizer = propertiesSanitizer this.evaluationContexts = evaluationContexts this.remoteConfig = remoteConfig if (beforeSend != null) { diff --git a/posthog/src/test/java/com/posthog/PostHogStatelessTest.kt b/posthog/src/test/java/com/posthog/PostHogStatelessTest.kt index d6c754195..00dddfffb 100644 --- a/posthog/src/test/java/com/posthog/PostHogStatelessTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogStatelessTest.kt @@ -1087,41 +1087,6 @@ internal class PostHogStatelessTest { assertTrue(mockLogger.messages.any { it.contains("Error in beforeSend function") }) } - @Test - fun `properties sanitizer is applied to events`() { - val mockQueue = MockQueue() - sut = createStatelessInstance() - config = - createConfig().apply { - @Suppress("DEPRECATION") - propertiesSanitizer = - PostHogPropertiesSanitizer { properties -> - properties.apply { - remove("sensitive") - put("sanitized", true) - } - } - } - - sut.setup(config) - sut.setMockQueue(mockQueue) - - sut.captureStateless( - "test", - "user123", - mapOf( - "safe" to "value", - "sensitive" to "secret", - ), - ) - - assertEquals(1, mockQueue.events.size) - val event = mockQueue.events.first() - assertEquals("value", event.properties!!["safe"]) - assertNull(event.properties!!["sensitive"]) - assertEquals(true, event.properties!!["sanitized"]) - } - @Test fun `feature flag called events are sent when feature flags accessed`() { val mockQueue = MockQueue() diff --git a/posthog/src/test/java/com/posthog/PostHogTest.kt b/posthog/src/test/java/com/posthog/PostHogTest.kt index 01286caba..5ce8ff3be 100644 --- a/posthog/src/test/java/com/posthog/PostHogTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogTest.kt @@ -97,7 +97,6 @@ internal class PostHogTest { remoteConfig: Boolean = false, surveys: Boolean = false, cachePreferences: PostHogPreferences? = PostHogMemoryPreferences(), - propertiesSanitizer: PostHogPropertiesSanitizer? = null, beforeSend: PostHogBeforeSend? = null, evaluationContexts: List? = null, context: PostHogContext? = null, @@ -121,7 +120,6 @@ internal class PostHogTest { this.sendFeatureFlagEvent = sendFeatureFlagEvent this.reuseAnonymousId = reuseAnonymousId this.cachePreferences = cachePreferences - this.propertiesSanitizer = propertiesSanitizer this.evaluationContexts = evaluationContexts this.remoteConfig = remoteConfig this.surveys = surveys @@ -1009,8 +1007,7 @@ internal class PostHogTest { } @Test - @Suppress("DEPRECATION") - fun `getFeatureFlagPayload returns the value after reloaded`() { + fun `getFeatureFlagResult returns the payload after reloaded`() { val http = mockHttp( response = @@ -1025,7 +1022,7 @@ internal class PostHogTest { remoteConfigExecutor.shutdownAndAwaitTermination() - assertTrue(sut.getFeatureFlagPayload("4535-funnel-bar-viz") as Boolean) + assertTrue(sut.getFeatureFlagResult("4535-funnel-bar-viz", sendFeatureFlagEvent = false)?.payload as Boolean) sut.close() } @@ -3068,48 +3065,6 @@ internal class PostHogTest { assertFalse(config.debug) } - @Suppress("DEPRECATION") - @Test - fun `sanitize properties`() { - val http = mockHttp() - val url = http.url("/") - - val propertiesSanitizer = - PostHogPropertiesSanitizer { properties -> - properties.apply { - remove("prop") - } - } - - val sut = getSut(url.toString(), preloadFeatureFlags = false, propertiesSanitizer = propertiesSanitizer) - - sut.capture( - EVENT, - DISTINCT_ID, - // contains "prop" - props, - userProperties = userProps, - userPropertiesSetOnce = userPropsOnce, - groups = groups, - ) - - queueExecutor.shutdownAndAwaitTermination() - - val request = http.takeRequest() - - assertEquals(1, http.requestCount) - val content = request.body.unGzip() - val batch = serializer.deserialize(content.reader()) - - assertEquals(PROJECT_TOKEN, batch.projectToken) - assertNotNull(batch.sentAt) - - val theEvent = batch.batch.first() - assertNull(theEvent.properties!!["prop"]) - - sut.close() - } - @Test fun `reset preserves the push dedupe id`() { val http = mockHttp() diff --git a/posthog/src/testFixtures/java/com/posthog/PostHogFake.kt b/posthog/src/testFixtures/java/com/posthog/PostHogFake.kt index 603a469b8..b76f57c4e 100644 --- a/posthog/src/testFixtures/java/com/posthog/PostHogFake.kt +++ b/posthog/src/testFixtures/java/com/posthog/PostHogFake.kt @@ -98,14 +98,6 @@ public class PostHogFake : PostHogInterface { return null } - @Deprecated("Use getFeatureFlagResult() instead, which returns the flag value and payload from a single evaluation") - override fun getFeatureFlagPayload( - key: String, - defaultValue: Any?, - ): Any? { - return null - } - override fun getFeatureFlagResult( key: String, sendFeatureFlagEvent: Boolean?, From 4d7c1fd4271e57ea5ebaa8b480166b05ed028850 Mon Sep 17 00:00:00 2001 From: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com> Date: Mon, 26 Oct 2026 19:09:44 -0400 Subject: [PATCH 2/4] chore!: return null for malformed feature flag payloads (#889) Matches posthog-ios 4.0 and the sdk-specs payload decoding contract. --- .changeset/android-v4-major.md | 1 + .../posthog/internal/PostHogRemoteConfig.kt | 40 ++++--- .../internal/PostHogFeatureFlagsTest.kt | 6 +- .../internal/PostHogFeatureFlagsV1Test.kt | 4 +- .../internal/PostHogRemoteConfigTest.kt | 101 ++++++++++++++++++ 5 files changed, 130 insertions(+), 22 deletions(-) diff --git a/.changeset/android-v4-major.md b/.changeset/android-v4-major.md index 1b6846771..e50a3684b 100644 --- a/.changeset/android-v4-major.md +++ b/.changeset/android-v4-major.md @@ -8,3 +8,4 @@ - **Breaking:** remove the deprecated `evaluationEnvironments` config option; use `evaluationContexts` instead - **Breaking:** remove the deprecated `getFeatureFlagPayload()`; use `getFeatureFlagResult(key)?.payload` instead - **Breaking:** remove the deprecated `PostHogSessionReplayConfig.debouncerDelayMs`; use `throttleDelayMs` instead +- **Breaking:** return `null` instead of the raw string for a malformed, empty or whitespace-only feature flag payload from `getFeatureFlagResult(key)?.payload`; payloads must be valid JSON (a JSON `null` payload now also reads as `null` instead of the string `"null"`) diff --git a/posthog/src/main/java/com/posthog/internal/PostHogRemoteConfig.kt b/posthog/src/main/java/com/posthog/internal/PostHogRemoteConfig.kt index 03ba9946a..af4d05fc0 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogRemoteConfig.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogRemoteConfig.kt @@ -1,5 +1,8 @@ package com.posthog.internal +import com.google.gson.stream.JsonReader +import com.google.gson.stream.JsonToken +import com.google.gson.stream.MalformedJsonException import com.posthog.FeatureFlagResult import com.posthog.PostHogConfig import com.posthog.PostHogInternal @@ -17,6 +20,7 @@ import com.posthog.internal.PostHogPreferences.Companion.PUSH import com.posthog.internal.PostHogPreferences.Companion.SESSION_REPLAY import com.posthog.internal.PostHogPreferences.Companion.SURVEYS import com.posthog.surveys.Survey +import java.io.StringReader import java.util.concurrent.ExecutorService import java.util.concurrent.atomic.AtomicBoolean @@ -1198,26 +1202,28 @@ public class PostHogRemoteConfig( } } - private fun normalizePayloads(featureFlagPayloads: Map?): Map { - val parsedPayloads = (featureFlagPayloads ?: mapOf()).toMutableMap() - - for (item in parsedPayloads) { - val value = item.value + private fun normalizePayloads(featureFlagPayloads: Map?): Map = + (featureFlagPayloads ?: mapOf()).mapValues { (key, value) -> + // payloads arrive as serialized JSON strings; other values are already decoded + if (value is String) decodePayload(key, value) else value + } - try { - // only try to parse if its a String, since the JSON values are stringified - if (value is String) { - // try to deserialize as Any? - config.serializer.deserializeString(value)?.let { - parsedPayloads[item.key] = it - } - } - } catch (ignored: Throwable) { - // if it fails, we keep the original value + // Strict JSON, so a malformed, empty or whitespace-only payload is no payload (null), never the raw string. + private fun decodePayload( + key: String, + serialized: String, + ): Any? = + try { + val reader = JsonReader(StringReader(serialized)) + val decoded = config.serializer.gson.getAdapter(Any::class.java).read(reader) + if (reader.peek() != JsonToken.END_DOCUMENT) { + throw MalformedJsonException("Unexpected data after the JSON value") } + decoded + } catch (e: Throwable) { + config.logger.log("Ignoring malformed payload for feature flag $key: $e") + null } - return parsedPayloads - } private fun normalizeFlagsResponse(flagsResponse: PostHogFlagsResponse): PostHogFlagsResponse { val flags = flagsResponse.flags diff --git a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt index eec9f7337..a03b735b5 100644 --- a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt +++ b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsTest.kt @@ -407,7 +407,7 @@ internal class PostHogFeatureFlagsTest { assertTrue(sut.getFeatureFlagPayload("theBoolean", defaultValue = null) as Boolean) assertNull(sut.getFeatureFlagPayload("theNull", defaultValue = null)) - assertEquals("[1, 2", sut.getFeatureFlagPayload("theBroken", defaultValue = null) as String) + assertNull(sut.getFeatureFlagPayload("theBroken", defaultValue = null)) } @Test @@ -465,7 +465,7 @@ internal class PostHogFeatureFlagsTest { """ { "featureFlags": {"flag1": true}, - "featureFlagPayloads": {"flag1": "payload1"} + "featureFlagPayloads": {"flag1": "\"payload1\""} } """.trimIndent(), ), @@ -515,7 +515,7 @@ internal class PostHogFeatureFlagsTest { """ { "featureFlags": {"flag1": true}, - "featureFlagPayloads": {"flag1": "payload1"} + "featureFlagPayloads": {"flag1": "\"payload1\""} } """.trimIndent(), ), diff --git a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsV1Test.kt b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsV1Test.kt index 7cb40c827..fd2fa4220 100644 --- a/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsV1Test.kt +++ b/posthog/src/test/java/com/posthog/internal/PostHogFeatureFlagsV1Test.kt @@ -270,7 +270,7 @@ internal class PostHogFeatureFlagsV1Test { assertTrue(sut.getFeatureFlagPayload("theBoolean", defaultValue = null) as Boolean) assertNull(sut.getFeatureFlagPayload("theNull", defaultValue = null)) - assertEquals("[1, 2", sut.getFeatureFlagPayload("theBroken", defaultValue = null) as String) + assertNull(sut.getFeatureFlagPayload("theBroken", defaultValue = null)) } @Test @@ -328,7 +328,7 @@ internal class PostHogFeatureFlagsV1Test { """ { "featureFlags": {"flag1": true}, - "featureFlagPayloads": {"flag1": "payload1"} + "featureFlagPayloads": {"flag1": "\"payload1\""} } """.trimIndent(), ), diff --git a/posthog/src/test/java/com/posthog/internal/PostHogRemoteConfigTest.kt b/posthog/src/test/java/com/posthog/internal/PostHogRemoteConfigTest.kt index 5701d46e4..0d9249fc1 100644 --- a/posthog/src/test/java/com/posthog/internal/PostHogRemoteConfigTest.kt +++ b/posthog/src/test/java/com/posthog/internal/PostHogRemoteConfigTest.kt @@ -3084,4 +3084,105 @@ internal class PostHogRemoteConfigTest { sut.clear() http.shutdown() } + + private fun loadFlagsWithPayloads( + payloads: Map, + logs: MutableList = mutableListOf(), + ): PostHogRemoteConfig { + val serializedPayloads = payloads.entries.joinToString(",") { (key, value) -> "\"$key\":${jsonString(value)}" } + val flags = payloads.keys.joinToString(",") { "\"$it\":\"blue\"" } + val http = + mockHttp( + response = + MockResponse().setBody( + """{"errorsWhileComputingFlags":false,"featureFlags":{$flags,"beta-ui":true},""" + + """"featureFlagPayloads":{$serializedPayloads,"beta-ui":"{\"color\":\"green\"}"}}""", + ), + ) + val sut = getSut(host = http.url("/").toString()) + config!!.logger = + object : PostHogLogger { + override fun log(message: String) { + logs.add(message) + } + + override fun isEnabled(): Boolean = true + } + + val latch = CountDownLatch(1) + sut.loadFeatureFlags( + "my_identify", + anonymousId = "anonId", + emptyMap(), + onFeatureFlags = PostHogOnFeatureFlags { latch.countDown() }, + ) + assertTrue(latch.await(5, TimeUnit.SECONDS), "flags load should complete") + http.shutdown() + return sut + } + + private fun jsonString(value: String): String = PostHogSerializer(PostHogConfig(PROJECT_TOKEN)).gson.toJson(value) + + @Test + fun `malformed, empty or whitespace payloads are null and keep the flag and sibling payloads`() { + for (input in listOf("{broken", "", " ", "hello", "{\"a\":1} trailing")) { + preferences.clear() + val logs = mutableListOf() + val sut = loadFlagsWithPayloads(mapOf("checkout" to input), logs) + + assertNull(sut.getFeatureFlagPayload("checkout"), "payload for input '$input'") + val result = sut.getFeatureFlagResult("checkout") + assertNotNull(result) + assertTrue(result.enabled) + assertEquals("blue", result.variant) + assertNull(result.payload) + assertEquals(mapOf("checkout" to "blue", "beta-ui" to true), sut.getFeatureFlags()) + assertEquals(mapOf("color" to "green"), sut.getFeatureFlagPayload("beta-ui")) + assertTrue(logs.any { "checkout" in it && "malformed" in it }, "logs for input '$input': $logs") + + // the cache holds no raw string either, so a later launch reads no payload + assertNull(getSut(host = "http://127.0.0.1:1/").getFeatureFlagPayload("checkout")) + } + } + + @Test + fun `valid JSON payloads keep their decoded value`() { + val sut = + loadFlagsWithPayloads( + mapOf( + "string" to "\"hello\"", + "empty" to "\"\"", + "false" to "false", + "zero" to "0", + "null" to "null", + "object" to "{\"color\":\"green\"}", + "array" to "[1,false]", + ), + ) + + assertEquals("hello", sut.getFeatureFlagPayload("string")) + assertEquals("", sut.getFeatureFlagPayload("empty")) + assertEquals(false, sut.getFeatureFlagPayload("false")) + assertEquals(0, sut.getFeatureFlagPayload("zero")) + assertNull(sut.getFeatureFlagPayload("null")) + assertEquals(mapOf("color" to "green"), sut.getFeatureFlagPayload("object")) + assertEquals(listOf(1, false), sut.getFeatureFlagPayload("array")) + } + + @Test + fun `bootstrapped string payloads are not JSON-decoded again`() { + for (payload in listOf("hello", "123", "true")) { + val sut = + getSut( + host = "http://127.0.0.1:1/", + bootstrap = + PostHogBootstrapConfig( + featureFlags = mapOf("beta-ui" to "variant-a"), + featureFlagPayloads = mapOf("beta-ui" to payload), + ), + ) + + assertEquals(payload, sut.getFeatureFlagResult("beta-ui")?.payload) + } + } } From ec06e867c6dae6181dce9bb9ab648ac01bc017fc Mon Sep 17 00:00:00 2001 From: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com> Date: Tue, 27 Oct 2026 12:06:10 -0400 Subject: [PATCH 3/4] chore!: make SDK-internal helpers non-public (#872) * chore!: make SDK-internal helpers non-public for posthog-android 4.0 Make internal the public declarations that no other module in this repo and no wrapper (Flutter, React Native, KMP) uses. LogLine and LogcatParser are Java, so they become package-private. Co-Authored-By: Claude Opus 5.5 (1M context) * fix: keep GzipRequestInterceptor public for the compliance adapter sdk_compliance_adapter is a separate Gradle module, only included by its Dockerfile, and constructs GzipRequestInterceptor directly. Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Opus 5.5 (1M context) --- .changeset/android-v4-major.md | 1 + posthog-android/api/posthog-android.api | 19 -------- .../android/internal/PostHogAndroidUtils.kt | 6 +-- .../android/replay/internal/LogLine.java | 4 +- .../android/replay/internal/LogcatParser.java | 4 +- posthog/api/posthog.api | 45 ------------------- .../internal/CustomHeadersInterceptor.kt | 4 +- .../com/posthog/internal/PostHogContext.kt | 3 +- .../PostHogLocalEvaluationDeserializers.kt | 7 +-- .../posthog/internal/PostHogPreferences.kt | 3 +- .../posthog/internal/PostHogPrintLogger.kt | 4 +- .../errortracking/ThrowableCoercer.kt | 4 +- .../posthog/internal/replay/RRDocumentNode.kt | 5 +-- .../replay/RRDomContentLoadedEvent.kt | 5 +-- .../posthog/internal/replay/RRLoadedEvent.kt | 5 +-- .../surveys/SurveyLanguageDetection.kt | 3 +- 16 files changed, 16 insertions(+), 106 deletions(-) diff --git a/.changeset/android-v4-major.md b/.changeset/android-v4-major.md index e50a3684b..5a2892275 100644 --- a/.changeset/android-v4-major.md +++ b/.changeset/android-v4-major.md @@ -9,3 +9,4 @@ - **Breaking:** remove the deprecated `getFeatureFlagPayload()`; use `getFeatureFlagResult(key)?.payload` instead - **Breaking:** remove the deprecated `PostHogSessionReplayConfig.debouncerDelayMs`; use `throttleDelayMs` instead - **Breaking:** return `null` instead of the raw string for a malformed, empty or whitespace-only feature flag payload from `getFeatureFlagResult(key)?.payload`; payloads must be valid JSON (a JSON `null` payload now also reads as `null` instead of the string `"null"`) +- **Breaking:** make SDK-internal helpers in the `com.posthog.internal` and `com.posthog.android.replay.internal` packages non-public diff --git a/posthog-android/api/posthog-android.api b/posthog-android/api/posthog-android.api index ee90e68c6..1749b4128 100644 --- a/posthog-android/api/posthog-android.api +++ b/posthog-android/api/posthog-android.api @@ -70,9 +70,6 @@ public final class com/posthog/android/internal/PostHogAndroidUtilsKt { public static final fun base64 (Landroid/graphics/Bitmap;Landroid/graphics/Bitmap$CompressFormat;I)Ljava/lang/String; public static synthetic fun base64$default (Landroid/graphics/Bitmap;Landroid/graphics/Bitmap$CompressFormat;IILjava/lang/Object;)Ljava/lang/String; public static final fun getApplicationInfo (Landroid/content/Context;)Landroid/content/pm/ApplicationInfo; - public static final fun isValid (Landroid/graphics/Bitmap;)Z - public static final fun webpBase64 (Landroid/graphics/Bitmap;I)Ljava/lang/String; - public static synthetic fun webpBase64$default (Landroid/graphics/Bitmap;IILjava/lang/Object;)Ljava/lang/String; } public abstract interface class com/posthog/android/replay/PostHogDrawableConverter { @@ -152,22 +149,6 @@ public final class com/posthog/android/replay/PostHogSessionReplayConfig { public final fun setVerifyScreenshotMaskAlignment (Z)V } -public class com/posthog/android/replay/internal/LogLine { - public field level Ljava/lang/String; - public field tag Ljava/lang/String; - public field text Ljava/lang/String; - public field time Ljava/util/GregorianCalendar; - public fun ()V -} - -public class com/posthog/android/replay/internal/LogcatParser { - public static final field BUFFER_BEGIN_RE Ljava/util/regex/Pattern; - public static final field DATE_TIME_MS_PATTERN Ljava/lang/String; - public static final field UTC Ljava/util/TimeZone; - public fun ()V - public fun parse (Ljava/lang/String;)Lcom/posthog/android/replay/internal/LogLine; -} - public final class com/posthog/android/surveys/PostHogSurveysIntegration : com/posthog/PostHogIntegration, com/posthog/internal/surveys/PostHogSurveysHandler { public fun (Landroid/content/Context;Lcom/posthog/PostHogConfig;)V public fun install (Lcom/posthog/PostHogInterface;)V diff --git a/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt b/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt index fd065353f..2468c870f 100644 --- a/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt +++ b/posthog-android/src/main/java/com/posthog/android/internal/PostHogAndroidUtils.kt @@ -196,8 +196,7 @@ public fun getApplicationInfo(context: Context): ApplicationInfo = .getApplicationInfo(context.packageName, GET_META_DATA) } -@PostHogInternal -public fun Bitmap.isValid(): Boolean { +internal fun Bitmap.isValid(): Boolean { return !isRecycled && width > 0 && height > 0 @@ -225,9 +224,8 @@ private fun Bitmap.base64BufferSize(): Int = .coerceAtLeast(4L * 1024) .toInt() -@PostHogInternal @Suppress("DEPRECATION") -public fun Bitmap.webpBase64(quality: Int = 30): String? { +internal fun Bitmap.webpBase64(quality: Int = 30): String? { if (!isValid()) { return null } diff --git a/posthog-android/src/main/java/com/posthog/android/replay/internal/LogLine.java b/posthog-android/src/main/java/com/posthog/android/replay/internal/LogLine.java index b85e3c251..249b00b79 100644 --- a/posthog-android/src/main/java/com/posthog/android/replay/internal/LogLine.java +++ b/posthog-android/src/main/java/com/posthog/android/replay/internal/LogLine.java @@ -2,15 +2,13 @@ package com.posthog.android.replay.internal; -import com.posthog.PostHogInternal; import java.util.GregorianCalendar; /** * A log line. */ -@PostHogInternal -public class LogLine { +class LogLine { /** * The timestamp of the event, parsed directly from Unix epoch time and represented in UTC. */ diff --git a/posthog-android/src/main/java/com/posthog/android/replay/internal/LogcatParser.java b/posthog-android/src/main/java/com/posthog/android/replay/internal/LogcatParser.java index 69c1d2e7a..b2cc5a9aa 100644 --- a/posthog-android/src/main/java/com/posthog/android/replay/internal/LogcatParser.java +++ b/posthog-android/src/main/java/com/posthog/android/replay/internal/LogcatParser.java @@ -2,7 +2,6 @@ package com.posthog.android.replay.internal; -import com.posthog.PostHogInternal; import java.util.GregorianCalendar; import java.util.TimeZone; @@ -10,8 +9,7 @@ import java.util.regex.Pattern; /** Parses logcat output formatted with {@code -v epoch}. */ -@PostHogInternal -public class LogcatParser { +class LogcatParser { /** UTC Time Zone. */ public static final TimeZone UTC = TimeZone.getTimeZone("UTC"); diff --git a/posthog/api/posthog.api b/posthog/api/posthog.api index c744204de..1d18f638e 100644 --- a/posthog/api/posthog.api +++ b/posthog/api/posthog.api @@ -606,11 +606,6 @@ public final class com/posthog/errortracking/PostHogExceptionStepsConfig { public final fun setMaxBytes (I)V } -public final class com/posthog/internal/CustomHeadersInterceptor : okhttp3/Interceptor { - public fun (Lcom/posthog/PostHogConfig;)V - public fun intercept (Lokhttp3/Interceptor$Chain;)Lokhttp3/Response; -} - public final class com/posthog/internal/EndpointSpec { public static final field Companion Lcom/posthog/internal/EndpointSpec$Companion; public static final fun batch (Lcom/posthog/PostHogConfig;Lcom/posthog/internal/PostHogApi;Ljava/lang/String;)Lcom/posthog/internal/EndpointSpec; @@ -827,10 +822,6 @@ public abstract interface class com/posthog/internal/PostHogContext { public abstract fun getStaticContext ()Ljava/util/Map; } -public final class com/posthog/internal/PostHogContextKt { - public static final fun personPropertiesContext (Lcom/posthog/internal/PostHogContext;)Ljava/util/Map; -} - public abstract interface class com/posthog/internal/PostHogDateProvider { public abstract fun addSecondsToCurrentDate (I)Ljava/util/Date; public abstract fun currentDate ()Ljava/util/Date; @@ -963,7 +954,6 @@ public abstract interface class com/posthog/internal/PostHogPreferences { public static final field GROUPS Ljava/lang/String; public static final field LAST_SEEN_SURVEY_DATE Ljava/lang/String; public static final field PUSH_OPENED_MESSAGE_IDS Ljava/lang/String; - public static final field PUSH_SUBSCRIPTION_REJECTED Ljava/lang/String; public static final field STRINGIFIED_KEYS Ljava/lang/String; public static final field SURVEY_PROGRESS Ljava/lang/String; public static final field SURVEY_SEEN Ljava/lang/String; @@ -986,7 +976,6 @@ public final class com/posthog/internal/PostHogPreferences$Companion { public static final field GROUPS Ljava/lang/String; public static final field LAST_SEEN_SURVEY_DATE Ljava/lang/String; public static final field PUSH_OPENED_MESSAGE_IDS Ljava/lang/String; - public static final field PUSH_SUBSCRIPTION_REJECTED Ljava/lang/String; public static final field STRINGIFIED_KEYS Ljava/lang/String; public static final field SURVEY_PROGRESS Ljava/lang/String; public static final field SURVEY_SEEN Ljava/lang/String; @@ -1000,13 +989,6 @@ public final class com/posthog/internal/PostHogPreferences$DefaultImpls { public static fun isAvailable (Lcom/posthog/internal/PostHogPreferences;)Z } -public final class com/posthog/internal/PostHogPrintLogger : com/posthog/internal/PostHogLogger { - public fun (Lcom/posthog/PostHogConfig;)V - public fun isEnabled ()Z - public fun log (Ljava/lang/String;)V - public fun logWarning (Ljava/lang/String;)V -} - public final class com/posthog/internal/PostHogQueue : com/posthog/internal/PostHogQueueInterface { public fun (Lcom/posthog/PostHogConfig;Lcom/posthog/internal/EndpointSpec;Ljava/util/concurrent/ExecutorService;)V public fun add (Ljava/lang/Object;)V @@ -1151,12 +1133,6 @@ public final class com/posthog/internal/PropertyGroup { public fun toString ()Ljava/lang/String; } -public final class com/posthog/internal/PropertyGroupDeserializer : com/google/gson/JsonDeserializer { - public fun ()V - public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lcom/posthog/internal/PropertyGroup; - public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object; -} - public final class com/posthog/internal/PropertyOperator : java/lang/Enum { public static final field Companion Lcom/posthog/internal/PropertyOperator$Companion; public static final field ENDS_WITH Lcom/posthog/internal/PropertyOperator; @@ -1242,12 +1218,6 @@ public final class com/posthog/internal/PropertyValue$PropertyGroups : com/posth public fun toString ()Ljava/lang/String; } -public final class com/posthog/internal/PropertyValueDeserializer : com/google/gson/JsonDeserializer { - public fun ()V - public fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Lcom/posthog/internal/PropertyValue; - public synthetic fun deserialize (Lcom/google/gson/JsonElement;Ljava/lang/reflect/Type;Lcom/google/gson/JsonDeserializationContext;)Ljava/lang/Object; -} - public final class com/posthog/internal/VariantDefinition { public fun (Ljava/lang/String;D)V public final fun getKey ()Ljava/lang/String; @@ -1260,8 +1230,6 @@ public final class com/posthog/internal/errortracking/ThrowableCoercer { public static final field MAX_EXCEPTION_LIST_SIZE I public static final field MAX_FRAMES_PER_STACKTRACE I public fun ()V - public final fun fromThrowableToPostHogProperties (Ljava/lang/Throwable;Ljava/util/List;Ljava/lang/String;Ljava/util/List;)Ljava/util/Map; - public static synthetic fun fromThrowableToPostHogProperties$default (Lcom/posthog/internal/errortracking/ThrowableCoercer;Ljava/lang/Throwable;Ljava/util/List;Ljava/lang/String;Ljava/util/List;ILjava/lang/Object;)Ljava/util/Map; } public abstract interface class com/posthog/internal/replay/PostHogSessionReplayHandler { @@ -1282,14 +1250,6 @@ public final class com/posthog/internal/replay/RRCustomEvent : com/posthog/inter public fun (Ljava/lang/String;Ljava/lang/Object;J)V } -public final class com/posthog/internal/replay/RRDocumentNode : com/posthog/internal/replay/RREvent { - public fun (Ljava/lang/String;Ljava/lang/Object;J)V -} - -public final class com/posthog/internal/replay/RRDomContentLoadedEvent : com/posthog/internal/replay/RREvent { - public fun (J)V -} - public class com/posthog/internal/replay/RREvent { public fun (Lcom/posthog/internal/replay/RREventType;JLjava/lang/Object;)V public synthetic fun (Lcom/posthog/internal/replay/RREventType;JLjava/lang/Object;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -1382,10 +1342,6 @@ public final class com/posthog/internal/replay/RRIncrementalSource$Companion { public final fun fromValue (I)Lcom/posthog/internal/replay/RRIncrementalSource; } -public final class com/posthog/internal/replay/RRLoadedEvent : com/posthog/internal/replay/RREvent { - public fun (J)V -} - public final class com/posthog/internal/replay/RRMetaEvent : com/posthog/internal/replay/RREvent { public fun (IIJLjava/lang/String;)V } @@ -1582,7 +1538,6 @@ public final class com/posthog/internal/surveys/ResolvedSurveyTranslations { public final class com/posthog/internal/surveys/SurveyLanguageDetectionKt { public static final fun detectSurveyLanguage (Ljava/lang/String;Ljava/util/Map;Ljava/lang/String;)Ljava/lang/String; - public static final fun findBestTranslationMatch (Ljava/util/Map;Ljava/lang/String;)Ljava/lang/String; } public final class com/posthog/internal/surveys/SurveyTranslationApplierKt { diff --git a/posthog/src/main/java/com/posthog/internal/CustomHeadersInterceptor.kt b/posthog/src/main/java/com/posthog/internal/CustomHeadersInterceptor.kt index 498937756..74cd4b3ef 100644 --- a/posthog/src/main/java/com/posthog/internal/CustomHeadersInterceptor.kt +++ b/posthog/src/main/java/com/posthog/internal/CustomHeadersInterceptor.kt @@ -1,7 +1,6 @@ package com.posthog.internal import com.posthog.PostHogConfig -import com.posthog.PostHogInternal import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.Interceptor import okhttp3.Request @@ -13,8 +12,7 @@ import java.io.IOException * reverse proxy) to every request the SDK sends. * @property config The Config */ -@PostHogInternal -public class CustomHeadersInterceptor(private val config: PostHogConfig) : Interceptor { +internal class CustomHeadersInterceptor(private val config: PostHogConfig) : Interceptor { private companion object { // SDK-managed headers that custom values can't override (compared lowercase). private val RESERVED_HEADERS = setOf("content-type", "user-agent", "accept-encoding", "content-encoding") diff --git a/posthog/src/main/java/com/posthog/internal/PostHogContext.kt b/posthog/src/main/java/com/posthog/internal/PostHogContext.kt index f0377d21d..ab7a528e8 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogContext.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogContext.kt @@ -19,8 +19,7 @@ public interface PostHogContext { * Returns person properties context by extracting relevant properties from static context. * This centralizes the logic for determining which properties should be used as person properties. */ -@PostHogInternal -public fun PostHogContext.personPropertiesContext(): Map { +internal fun PostHogContext.personPropertiesContext(): Map { val sdkInfo = getSdkInfo() val staticCtx = getStaticContext() val personProperties = mutableMapOf() diff --git a/posthog/src/main/java/com/posthog/internal/PostHogLocalEvaluationDeserializers.kt b/posthog/src/main/java/com/posthog/internal/PostHogLocalEvaluationDeserializers.kt index 2b5ec9df4..00117cbbc 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogLocalEvaluationDeserializers.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogLocalEvaluationDeserializers.kt @@ -6,14 +6,12 @@ import com.google.gson.JsonElement import com.google.gson.JsonSerializationContext import com.google.gson.JsonSerializer import com.google.gson.stream.MalformedJsonException -import com.posthog.PostHogInternal import java.lang.reflect.Type /** * Gson deserializer for PropertyGroup */ -@PostHogInternal -public class PropertyGroupDeserializer : JsonDeserializer { +internal class PropertyGroupDeserializer : JsonDeserializer { override fun deserialize( json: JsonElement, typeOfT: Type, @@ -43,8 +41,7 @@ public class PropertyGroupDeserializer : JsonDeserializer { * Gson deserializer for PropertyValue sealed interface * Determines whether to deserialize as PropertyGroups or FlagProperties based on structure */ -@PostHogInternal -public class PropertyValueDeserializer : JsonDeserializer { +internal class PropertyValueDeserializer : JsonDeserializer { override fun deserialize( json: JsonElement, typeOfT: Type, diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPreferences.kt b/posthog/src/main/java/com/posthog/internal/PostHogPreferences.kt index 84cf4d343..9d29d9f99 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogPreferences.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogPreferences.kt @@ -56,8 +56,7 @@ public interface PostHogPreferences { @PostHogInternal public const val PUSH_OPENED_MESSAGE_IDS: String = "pushOpenedMessageIds" - @PostHogInternal - public const val PUSH_SUBSCRIPTION_REJECTED: String = "pushSubscriptionRejected" + internal const val PUSH_SUBSCRIPTION_REJECTED: String = "pushSubscriptionRejected" internal const val PERSON_PROPERTIES_FOR_FLAGS = "personPropertiesForFlags" internal const val GROUP_PROPERTIES_FOR_FLAGS = "groupPropertiesForFlags" public const val SURVEY_PROGRESS: String = "surveyProgress" diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPrintLogger.kt b/posthog/src/main/java/com/posthog/internal/PostHogPrintLogger.kt index babf8f72d..4c2d1f643 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogPrintLogger.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogPrintLogger.kt @@ -1,14 +1,12 @@ package com.posthog.internal import com.posthog.PostHogConfig -import com.posthog.PostHogInternal /** * Logs the messages using System.out only if config.debug is enabled * @property config the Config */ -@PostHogInternal -public class PostHogPrintLogger(private val config: PostHogConfig) : PostHogLogger { +internal class PostHogPrintLogger(private val config: PostHogConfig) : PostHogLogger { override fun log(message: String) { // isEnabled can be abstracted in another class (refactor needed). if (isEnabled()) { diff --git a/posthog/src/main/java/com/posthog/internal/errortracking/ThrowableCoercer.kt b/posthog/src/main/java/com/posthog/internal/errortracking/ThrowableCoercer.kt index 89b18a828..fb67d2880 100644 --- a/posthog/src/main/java/com/posthog/internal/errortracking/ThrowableCoercer.kt +++ b/posthog/src/main/java/com/posthog/internal/errortracking/ThrowableCoercer.kt @@ -244,9 +244,7 @@ public class ThrowableCoercer { return exception } - // `inAppExcludes` is appended after `releaseIdentifier` so existing positional Kotlin calls stay - // valid. This is a `@PostHogInternal` entry point, so the JVM descriptor change is fine. - public fun fromThrowableToPostHogProperties( + internal fun fromThrowableToPostHogProperties( throwable: Throwable, inAppIncludes: List = listOf(), releaseIdentifier: String? = null, diff --git a/posthog/src/main/java/com/posthog/internal/replay/RRDocumentNode.kt b/posthog/src/main/java/com/posthog/internal/replay/RRDocumentNode.kt index d45502d16..c635252d3 100644 --- a/posthog/src/main/java/com/posthog/internal/replay/RRDocumentNode.kt +++ b/posthog/src/main/java/com/posthog/internal/replay/RRDocumentNode.kt @@ -1,9 +1,6 @@ package com.posthog.internal.replay -import com.posthog.PostHogInternal - -@PostHogInternal -public class RRDocumentNode(tag: String, payload: Any, timestamp: Long) : RREvent( +internal class RRDocumentNode(tag: String, payload: Any, timestamp: Long) : RREvent( type = RREventType.Custom, data = mapOf( diff --git a/posthog/src/main/java/com/posthog/internal/replay/RRDomContentLoadedEvent.kt b/posthog/src/main/java/com/posthog/internal/replay/RRDomContentLoadedEvent.kt index 6b550d57d..275be367f 100644 --- a/posthog/src/main/java/com/posthog/internal/replay/RRDomContentLoadedEvent.kt +++ b/posthog/src/main/java/com/posthog/internal/replay/RRDomContentLoadedEvent.kt @@ -1,9 +1,6 @@ package com.posthog.internal.replay -import com.posthog.PostHogInternal - -@PostHogInternal -public class RRDomContentLoadedEvent(timestamp: Long) : RREvent( +internal class RRDomContentLoadedEvent(timestamp: Long) : RREvent( type = RREventType.DomContentLoaded, timestamp = timestamp, ) diff --git a/posthog/src/main/java/com/posthog/internal/replay/RRLoadedEvent.kt b/posthog/src/main/java/com/posthog/internal/replay/RRLoadedEvent.kt index 31508abd4..3b02f7844 100644 --- a/posthog/src/main/java/com/posthog/internal/replay/RRLoadedEvent.kt +++ b/posthog/src/main/java/com/posthog/internal/replay/RRLoadedEvent.kt @@ -1,9 +1,6 @@ package com.posthog.internal.replay -import com.posthog.PostHogInternal - -@PostHogInternal -public class RRLoadedEvent(timestamp: Long) : RREvent( +internal class RRLoadedEvent(timestamp: Long) : RREvent( type = RREventType.Load, timestamp = timestamp, ) diff --git a/posthog/src/main/java/com/posthog/internal/surveys/SurveyLanguageDetection.kt b/posthog/src/main/java/com/posthog/internal/surveys/SurveyLanguageDetection.kt index 9f2186e7b..a8f3814f9 100644 --- a/posthog/src/main/java/com/posthog/internal/surveys/SurveyLanguageDetection.kt +++ b/posthog/src/main/java/com/posthog/internal/surveys/SurveyLanguageDetection.kt @@ -36,8 +36,7 @@ public fun detectSurveyLanguage( * Returns the original-cased key from [translations] (so it can be reported verbatim * as `$survey_language`), or `null` if no match. */ -@PostHogInternal -public fun findBestTranslationMatch( +internal fun findBestTranslationMatch( translations: Map?, targetLanguage: String?, ): String? { From ead5a23ce12b90c2d886535dba48458149a2718e Mon Sep 17 00:00:00 2001 From: Anna Garcia <11654201+turnipdabeets@users.noreply.github.com> Date: Tue, 27 Oct 2026 12:24:57 -0400 Subject: [PATCH 4/4] chore!: remove the deprecated remoteConfig option (#874) Setup now always loads /config. With remoteConfig = false, setup used to skip /config, which left remote settings unloaded since /flags stopped returning config. Co-authored-by: Claude Opus 5.5 (1M context) --- .changeset/android-v4-major.md | 1 + .../compose/internal/PostHogSurveyHostTest.kt | 1 - .../replay/PostHogReplayIntegration.kt | 10 +--- .../android/PostHogAndroidAppVersionTest.kt | 3 +- .../PostHogAndroidEventSnapshotsTest.kt | 12 +++-- .../internal/InteractionActivityTest.kt | 4 +- .../InteractionDetectorActivityTest.kt | 3 +- .../InteractionDetectorIntegrationTest.kt | 6 +-- .../replay/PostHogReplayIntegrationTest.kt | 51 +------------------ .../surveys/PostHogSurveysRestartTest.kt | 1 - .../java/com/posthog/server/PostHogConfig.kt | 1 - .../com/posthog/server/PostHogConfigTest.kt | 1 - posthog/api/posthog.api | 6 +-- posthog/src/main/java/com/posthog/PostHog.kt | 14 ++--- .../main/java/com/posthog/PostHogConfig.kt | 9 ---- .../java/com/posthog/PostHogBeforeSendTest.kt | 1 - .../java/com/posthog/PostHogBootstrapTest.kt | 9 ++-- .../com/posthog/PostHogFeatureFlagsTest.kt | 5 +- .../posthog/PostHogIntegrationChangeTest.kt | 3 +- .../PostHogIntegrationInstallFailureTest.kt | 3 +- .../posthog/PostHogOkHttpInterceptorTest.kt | 1 - .../src/test/java/com/posthog/PostHogTest.kt | 48 ++++++++--------- 22 files changed, 51 insertions(+), 142 deletions(-) diff --git a/.changeset/android-v4-major.md b/.changeset/android-v4-major.md index 5a2892275..e89b373a7 100644 --- a/.changeset/android-v4-major.md +++ b/.changeset/android-v4-major.md @@ -10,3 +10,4 @@ - **Breaking:** remove the deprecated `PostHogSessionReplayConfig.debouncerDelayMs`; use `throttleDelayMs` instead - **Breaking:** return `null` instead of the raw string for a malformed, empty or whitespace-only feature flag payload from `getFeatureFlagResult(key)?.payload`; payloads must be valid JSON (a JSON `null` payload now also reads as `null` instead of the string `"null"`) - **Breaking:** make SDK-internal helpers in the `com.posthog.internal` and `com.posthog.android.replay.internal` packages non-public +- **Breaking:** remove the deprecated `remoteConfig` config option; the SDK now always loads remote config at setup diff --git a/posthog-android-surveys-compose/src/testDebug/java/com/posthog/android/surveys/compose/internal/PostHogSurveyHostTest.kt b/posthog-android-surveys-compose/src/testDebug/java/com/posthog/android/surveys/compose/internal/PostHogSurveyHostTest.kt index 16b3ee565..fe5b5a8a1 100644 --- a/posthog-android-surveys-compose/src/testDebug/java/com/posthog/android/surveys/compose/internal/PostHogSurveyHostTest.kt +++ b/posthog-android-surveys-compose/src/testDebug/java/com/posthog/android/surveys/compose/internal/PostHogSurveyHostTest.kt @@ -279,7 +279,6 @@ internal class PostHogSurveyHostTest { PostHogConfig("host-reset", "http://127.0.0.1:1").apply { cachePreferences = PostHogMemoryPreferences() preloadFeatureFlags = false - remoteConfig = false reuseAnonymousId = true } } diff --git a/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayIntegration.kt b/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayIntegration.kt index daec3775b..46af13767 100644 --- a/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayIntegration.kt +++ b/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayIntegration.kt @@ -3054,15 +3054,9 @@ public class PostHogReplayIntegration( } // Whether to buffer until the live remote config resolves: only when a remote config holder - // exists and hasn't resolved yet, AND a fetch will actually be attempted at setup. If neither - // remoteConfig nor preloadFeatureFlags is enabled, PostHog.setup dispatches no /config or /flags - // request, so no onRemoteConfig callback ever arrives to disarm the gate — - // buffering would then grow unbounded for the whole session. Don't arm in that case. + // exists and hasn't resolved yet. PostHog.setup always requests /config, so the + // onRemoteConfig callback disarms the gate. private fun shouldAwaitFirstRemoteConfig(): Boolean { - @Suppress("DEPRECATION") - if (!config.remoteConfig && !config.preloadFeatureFlags) { - return false - } return config.remoteConfigHolder?.let { !it.hasRemoteConfigFetched() } ?: false } diff --git a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidAppVersionTest.kt b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidAppVersionTest.kt index 9b16bcb53..68518c7df 100644 --- a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidAppVersionTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidAppVersionTest.kt @@ -43,10 +43,9 @@ internal class PostHogAndroidAppVersionTest { events: MutableList, configure: PostHogAndroidConfig.() -> Unit = {}, ): PostHogAndroidConfig = - PostHogAndroidConfig(projectToken).apply { + PostHogAndroidConfig(projectToken, "http://127.0.0.1:1").apply { cachePreferences = preferences captureApplicationLifecycleEvents = captureLifecycle - remoteConfig = false preloadFeatureFlags = false configure() addBeforeSend { diff --git a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidEventSnapshotsTest.kt b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidEventSnapshotsTest.kt index e59c0977c..ae466718a 100644 --- a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidEventSnapshotsTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidEventSnapshotsTest.kt @@ -94,7 +94,7 @@ internal class PostHogAndroidEventSnapshotsTest { sut.captureException(fixedThrowable(), mapOf("handled" to true, "component" to "checkout")) sut.flush() - val request = fixture.server.takeRequests("/flags/?v=2", "/batch").single { it.path == "/batch" } + val request = fixture.server.takeRequests(CONFIG_PATH, "/flags/?v=2", "/batch").single { it.path == "/batch" } val snapshot = requestSnapshot(request, fixture.config) normalizeBatchVolatileFields(snapshot) @@ -118,7 +118,7 @@ internal class PostHogAndroidEventSnapshotsTest { fixture.client.reloadFeatureFlags(PostHogOnFeatureFlags { loaded.countDown() }) assertTrue(loaded.await(10, TimeUnit.SECONDS), "Feature flags request did not finish") - val request = fixture.server.takeRequests("/flags/?v=2").single() + val request = fixture.server.takeRequests(CONFIG_PATH, "/flags/?v=2").single { it.path == "/flags/?v=2" } assertSnapshot("flags-request.json", requestSnapshot(request, fixture.config), fixture.config) } @@ -152,7 +152,7 @@ internal class PostHogAndroidEventSnapshotsTest { ) fixture.client.flush() - val request = fixture.server.takeRequests("/s/").single() + val request = fixture.server.takeRequests(CONFIG_PATH, "/s/").single { it.path == "/s/" } val snapshot = requestSnapshot(request, fixture.config) normalizeReplayVolatileFields(snapshot) @@ -163,6 +163,8 @@ internal class PostHogAndroidEventSnapshotsTest { mockContextAppStart(context, tmpDir) val server = MockWebServer().apply { + // setup always requests /config first + enqueue(MockResponse().setBody(CONFIG_RESPONSE).setHeader("Content-Type", "application/json")) repeat(10) { enqueue(MockResponse().setBody(FLAGS_RESPONSE).setHeader("Content-Type", "application/json")) } @@ -185,8 +187,6 @@ internal class PostHogAndroidEventSnapshotsTest { flushIntervalSeconds = 60 maxBatchSize = 20 preloadFeatureFlags = false - @Suppress("DEPRECATION") - remoteConfig = false captureApplicationLifecycleEvents = false captureDeepLinks = false captureScreenViews = false @@ -412,6 +412,8 @@ internal class PostHogAndroidEventSnapshotsTest { private val FIXED_DATE = Date(FIXED_MILLIS) private val SESSION_ID = UUID.fromString("018bcfe5-687b-7abc-8def-0123456789ab") private const val FIXED_TIMESTAMP = "2023-11-14T22:13:20.123Z" + private const val CONFIG_PATH = "/array/$PROJECT_TOKEN/config" + private const val CONFIG_RESPONSE = """{"hasFeatureFlags":true}""" private const val FLAGS_RESPONSE = """{ "flags": { diff --git a/posthog-android/src/test/java/com/posthog/android/internal/InteractionActivityTest.kt b/posthog-android/src/test/java/com/posthog/android/internal/InteractionActivityTest.kt index 9e7a8dc2b..f888f61e3 100644 --- a/posthog-android/src/test/java/com/posthog/android/internal/InteractionActivityTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/internal/InteractionActivityTest.kt @@ -46,7 +46,7 @@ internal class InteractionActivityTest { fun `real Compose and embedded Views capture through window with replay off`() { val events = CopyOnWriteArrayList() val config = - PostHogAndroidConfig("test").apply { + PostHogAndroidConfig("test", "http://127.0.0.1:1").apply { captureElementInteractions = true sessionReplay = false captureApplicationLifecycleEvents = false @@ -54,8 +54,6 @@ internal class InteractionActivityTest { captureDeepLinks = false capturePushNotificationOpened = false capturePushNotificationSubscriptions = false - @Suppress("DEPRECATION") // Test isolation: do not fetch remote configuration. - remoteConfig = false preloadFeatureFlags = false addBeforeSend { event -> events += event diff --git a/posthog-android/src/test/java/com/posthog/android/internal/InteractionDetectorActivityTest.kt b/posthog-android/src/test/java/com/posthog/android/internal/InteractionDetectorActivityTest.kt index a380c5325..7091f75f4 100644 --- a/posthog-android/src/test/java/com/posthog/android/internal/InteractionDetectorActivityTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/internal/InteractionDetectorActivityTest.kt @@ -53,7 +53,7 @@ internal class InteractionDetectorActivityTest { @Suppress("DEPRECATION") private fun setup() { val config = - PostHogAndroidConfig("test").apply { + PostHogAndroidConfig("test", "http://127.0.0.1:1").apply { captureRageClicks = true captureDeadClicks = true sessionReplay = false @@ -62,7 +62,6 @@ internal class InteractionDetectorActivityTest { captureDeepLinks = false capturePushNotificationOpened = false capturePushNotificationSubscriptions = false - remoteConfig = false preloadFeatureFlags = false addBeforeSend { event -> events += event diff --git a/posthog-android/src/test/java/com/posthog/android/internal/InteractionDetectorIntegrationTest.kt b/posthog-android/src/test/java/com/posthog/android/internal/InteractionDetectorIntegrationTest.kt index 90d2b7c89..730e1d45f 100644 --- a/posthog-android/src/test/java/com/posthog/android/internal/InteractionDetectorIntegrationTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/internal/InteractionDetectorIntegrationTest.kt @@ -48,12 +48,11 @@ internal class InteractionDetectorIntegrationTest { @Suppress("DEPRECATION") private val config = - PostHogAndroidConfig(PROJECT_TOKEN).apply { + PostHogAndroidConfig(PROJECT_TOKEN, "http://127.0.0.1:1").apply { captureRageClicks = true captureDeadClicks = true sessionReplay = false cachePreferences = PostHogMemoryPreferences() - remoteConfig = false preloadFeatureFlags = false addBeforeSend { event -> if (event.event in listOf("\$rageclick", "\$dead_click", "\$autocapture")) events += event @@ -253,9 +252,8 @@ internal class InteractionDetectorIntegrationTest { fun `secondary session changes use normal capture context when owner is not notified`() { val secondary = PostHog.with( - PostHogAndroidConfig("secondary-test-key").apply { + PostHogAndroidConfig("secondary-test-key", "http://127.0.0.1:1").apply { cachePreferences = PostHogMemoryPreferences() - remoteConfig = false preloadFeatureFlags = false addBeforeSend { null } }, diff --git a/posthog-android/src/test/java/com/posthog/android/replay/PostHogReplayIntegrationTest.kt b/posthog-android/src/test/java/com/posthog/android/replay/PostHogReplayIntegrationTest.kt index fcf0b0a72..a4571077a 100644 --- a/posthog-android/src/test/java/com/posthog/android/replay/PostHogReplayIntegrationTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/replay/PostHogReplayIntegrationTest.kt @@ -2127,53 +2127,6 @@ internal class PostHogReplayIntegrationTest { } } - @Test - fun `gate does not arm when neither remoteConfig nor preloadFeatureFlags will fetch`() { - // With both remoteConfig and preloadFeatureFlags disabled, setup dispatches no /config or - // /flags request, so no onRemoteConfig callback arrives to disarm the gate. Arming it would - // buffer every snapshot for the whole session; instead it must stay disarmed and persist. - val remoteConfigMock = - mock { - on { isSessionReplayFlagActive() } doReturn true - on { hasRemoteConfigFetched() } doReturn false - on { makeSamplingDecision(any()) } doReturn true - on { getEventTriggers() } doReturn emptySet() - on { getRecordingMinimumDurationMs() } doReturn null - } - val config = - PostHogAndroidConfig(PROJECT_TOKEN).apply { - remoteConfigHolder = remoteConfigMock - sessionReplay = true - @Suppress("DEPRECATION") - remoteConfig = false - preloadFeatureFlags = false - } - val storagePrefix = tmpDir.newFolder().absolutePath - val executor = createReplayExecutor() - val innerQueue = - PostHogQueue( - config, - EndpointSpec.snapshot(config, PostHogApi(config), storagePrefix), - executor, - ) - val replayQueue = PostHogReplayQueue(config, innerQueue, storagePrefix, executor) - config.replayQueueHolder = replayQueue - val sut = PostHogReplayIntegration(mock(), config, MainHandler()) - val postHog = mock() - whenever(postHog.getSessionId()).thenReturn(UUID.randomUUID()) - sut.install(postHog) - sut.start(resumeCurrent = true) - try { - // No remote-config callback will ever fire, so buffering must not be armed. - replayQueue.add(createTestEvent("snapshot_1")) - awaitCondition { replayQueue.depth == 1 } - assertEquals(0, replayQueue.bufferDepth) - assertEquals(1, replayQueue.depth) - } finally { - sut.uninstall() - } - } - @Test fun `mid-session resume forces a fresh keyframe by clearing per-view snapshot state`() { // A mid-session flag-on resume must emit a fresh full snapshot: while stopped, per-view state @@ -2599,9 +2552,7 @@ internal class PostHogReplayIntegrationTest { @Test @Config(sdk = [26], shadows = [SnapshotBoundaryShadowPixelCopy::class]) fun `committed snapshot keeps its session when rotation precedes core enrichment`() { - val config = PostHogAndroidConfig(PROJECT_TOKEN) - @Suppress("DEPRECATION") - config.remoteConfig = false + val config = PostHogAndroidConfig(PROJECT_TOKEN, "http://127.0.0.1:1") config.preloadFeatureFlags = false var captured: PostHogEvent? = null config.addBeforeSend { event -> diff --git a/posthog-android/src/test/java/com/posthog/android/surveys/PostHogSurveysRestartTest.kt b/posthog-android/src/test/java/com/posthog/android/surveys/PostHogSurveysRestartTest.kt index cb2c57ea5..16521bde8 100644 --- a/posthog-android/src/test/java/com/posthog/android/surveys/PostHogSurveysRestartTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/surveys/PostHogSurveysRestartTest.kt @@ -139,7 +139,6 @@ internal class PostHogSurveysRestartTest { PostHogAndroidConfig(projectToken), ) preloadFeatureFlags = false - remoteConfig = false surveys = true surveysConfig.surveysDelegate = renderer surveysConfig.overrideDisplayLanguage = language diff --git a/posthog-server/src/main/java/com/posthog/server/PostHogConfig.kt b/posthog-server/src/main/java/com/posthog/server/PostHogConfig.kt index 264e46bee..e0adad853 100644 --- a/posthog-server/src/main/java/com/posthog/server/PostHogConfig.kt +++ b/posthog-server/src/main/java/com/posthog/server/PostHogConfig.kt @@ -306,7 +306,6 @@ public open class PostHogConfig constructor( debug = debug, sendFeatureFlagEvent = sendFeatureFlagEvent, preloadFeatureFlags = preloadFeatureFlags, - remoteConfig = remoteConfig, flushAt = flushAt, maxQueueSize = maxQueueSize, maxBatchSize = maxBatchSize, diff --git a/posthog-server/src/test/java/com/posthog/server/PostHogConfigTest.kt b/posthog-server/src/test/java/com/posthog/server/PostHogConfigTest.kt index 6d36783ea..b68787d37 100644 --- a/posthog-server/src/test/java/com/posthog/server/PostHogConfigTest.kt +++ b/posthog-server/src/test/java/com/posthog/server/PostHogConfigTest.kt @@ -175,7 +175,6 @@ internal class PostHogConfigTest { assertEquals(true, coreConfig.debug) assertEquals(false, coreConfig.sendFeatureFlagEvent) assertEquals(false, coreConfig.preloadFeatureFlags) - assertEquals(false, coreConfig.remoteConfig) assertEquals(15, coreConfig.flushAt) assertEquals(750, coreConfig.maxQueueSize) assertEquals(30, coreConfig.maxBatchSize) diff --git a/posthog/api/posthog.api b/posthog/api/posthog.api index 1d18f638e..d515cea20 100644 --- a/posthog/api/posthog.api +++ b/posthog/api/posthog.api @@ -156,8 +156,8 @@ public class com/posthog/PostHogConfig { public static final field DEFAULT_MAX_QUEUE_SIZE I public static final field DEFAULT_US_ASSETS_HOST Ljava/lang/String; public static final field DEFAULT_US_HOST Ljava/lang/String; - public fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;ZZZIZLjava/util/List;ZIIIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;ZLkotlin/jvm/functions/Function1;ZLcom/posthog/PersonProfiles;ZLjava/net/Proxy;Lcom/posthog/surveys/PostHogSurveysConfig;Lcom/posthog/logs/PostHogLogsConfig;Lkotlin/jvm/functions/Function6;Lkotlin/jvm/functions/Function5;Lcom/posthog/errortracking/PostHogErrorTrackingConfig;Ljava/lang/String;Lcom/posthog/PostHogBootstrapConfig;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun addBeforeSend (Lcom/posthog/PostHogBeforeSend;)V public final fun addIntegration (Lcom/posthog/PostHogIntegration;)V public final fun getBeforeSendList ()Ljava/util/List; @@ -196,7 +196,6 @@ public class com/posthog/PostHogConfig { public final fun getPushIdentityProvider ()Lkotlin/jvm/functions/Function3; public final fun getQueueProvider ()Lkotlin/jvm/functions/Function5; public final fun getReleaseIdentifier ()Ljava/lang/String; - public final fun getRemoteConfig ()Z public final fun getRemoteConfigHolder ()Lcom/posthog/internal/PostHogRemoteConfig; public final fun getRemoteConfigProvider ()Lkotlin/jvm/functions/Function6; public final fun getReplayStoragePrefix ()Ljava/lang/String; @@ -246,7 +245,6 @@ public class com/posthog/PostHogConfig { public final fun setProxy (Ljava/net/Proxy;)V public final fun setPushIdentityProvider (Lkotlin/jvm/functions/Function3;)V public final fun setReleaseIdentifier (Ljava/lang/String;)V - public final fun setRemoteConfig (Z)V public final fun setRemoteConfigHolder (Lcom/posthog/internal/PostHogRemoteConfig;)V public final fun setReplayStoragePrefix (Ljava/lang/String;)V public final fun setRequestHeaders (Ljava/util/Map;)V diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index 6bd98ff1e..a616e878c 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -393,7 +393,6 @@ public class PostHog private constructor( } // only because of testing in isolation, this flag is always enabled - @Suppress("DEPRECATION") if (reloadFeatureFlags) { // Bootstrap already seeded the flags in RemoteConfig's init; fire the flags-loaded // callbacks now so listeners aren't blocked on the first network response. @@ -401,15 +400,10 @@ public class PostHog private constructor( notifyFeatureFlagsCallback(internalOnFeatureFlagsLoaded) notifyFeatureFlagsCallback(config.onFeatureFlags) } - when { - config.remoteConfig -> - loadRemoteConfigRequest( - internalOnFeatureFlagsLoaded, - config.onFeatureFlags, - ) - - config.preloadFeatureFlags -> reloadFeatureFlags(config.onFeatureFlags) - } + loadRemoteConfigRequest( + internalOnFeatureFlagsLoaded, + config.onFeatureFlags, + ) } // Reconcile a differing identified bootstrap after the setup reload is dispatched. diff --git a/posthog/src/main/java/com/posthog/PostHogConfig.kt b/posthog/src/main/java/com/posthog/PostHogConfig.kt index c01078500..c72ffa1e4 100644 --- a/posthog/src/main/java/com/posthog/PostHogConfig.kt +++ b/posthog/src/main/java/com/posthog/PostHogConfig.kt @@ -108,15 +108,6 @@ public open class PostHogConfig( * Default: true */ public var setDefaultPersonProperties: Boolean = true, - /** - * Preload PostHog remote config automatically - * Defaults to true - */ - @Deprecated( - message = "Remote config is now always enabled. This option is a no-op and will be removed in a future version.", - level = DeprecationLevel.WARNING, - ) - public var remoteConfig: Boolean = true, /** * Number of minimum events before they are sent over the wire * Defaults to 20 diff --git a/posthog/src/test/java/com/posthog/PostHogBeforeSendTest.kt b/posthog/src/test/java/com/posthog/PostHogBeforeSendTest.kt index 454282022..e4bbba3c1 100644 --- a/posthog/src/test/java/com/posthog/PostHogBeforeSendTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogBeforeSendTest.kt @@ -34,7 +34,6 @@ internal class PostHogBeforeSendTest { storagePrefix = tmpDir.newFolder().absolutePath replayStoragePrefix = tmpDir.newFolder().absolutePath preloadFeatureFlags = false - remoteConfig = false cachePreferences = PostHogMemoryPreferences() hooks.forEach { addBeforeSend(it) } } diff --git a/posthog/src/test/java/com/posthog/PostHogBootstrapTest.kt b/posthog/src/test/java/com/posthog/PostHogBootstrapTest.kt index b4d4c80b1..214256e76 100644 --- a/posthog/src/test/java/com/posthog/PostHogBootstrapTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogBootstrapTest.kt @@ -30,13 +30,13 @@ internal class PostHogBootstrapTest { private val serializer = PostHogSerializer(PostHogConfig(PROJECT_TOKEN)) private lateinit var config: PostHogConfig - @Suppress("DEPRECATION") private fun getSut( host: String = PostHogConfig.DEFAULT_HOST, bootstrap: PostHogBootstrapConfig? = null, cachePreferences: PostHogMemoryPreferences = PostHogMemoryPreferences(), preloadFeatureFlags: Boolean = false, - reloadFeatureFlags: Boolean = true, + // setup always requests /config; skip it when the test doesn't preload flags + reloadFeatureFlags: Boolean = preloadFeatureFlags, sendFeatureFlagEvent: Boolean = true, flushAt: Int = 1, storagePrefix: String = tmpDir.newFolder().absolutePath, @@ -52,8 +52,6 @@ internal class PostHogBootstrapTest { this.preloadFeatureFlags = preloadFeatureFlags this.sendFeatureFlagEvent = sendFeatureFlagEvent this.cachePreferences = cachePreferences - // keep setup on the single /flags path instead of the /config + /flags path - this.remoteConfig = false this.bootstrap = bootstrap this.optOut = optOut this.onFeatureFlags = onFeatureFlags @@ -370,6 +368,9 @@ internal class PostHogBootstrapTest { var loaded = false val sut = getSut( + host = "http://127.0.0.1:1", + // the callback fires from setup's flag loading, before /config answers + reloadFeatureFlags = true, bootstrap = PostHogBootstrapConfig(featureFlags = mapOf("beta-ui" to true)), onFeatureFlags = PostHogOnFeatureFlags { loaded = true }, ) diff --git a/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt b/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt index 82575e966..15122e4fe 100644 --- a/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt @@ -48,11 +48,11 @@ internal class PostHogFeatureFlagsTest { storagePrefix: String = tmpDir.newFolder().absolutePath, optOut: Boolean = false, preloadFeatureFlags: Boolean = true, - reloadFeatureFlags: Boolean = true, + // setup always requests /config; skip it when the test doesn't preload flags + reloadFeatureFlags: Boolean = preloadFeatureFlags, sendFeatureFlagEvent: Boolean = true, reuseAnonymousId: Boolean = false, integration: PostHogIntegration? = null, - remoteConfig: Boolean = false, cachePreferences: PostHogMemoryPreferences = PostHogMemoryPreferences(), beforeSend: PostHogBeforeSend? = null, evaluationContexts: List? = null, @@ -73,7 +73,6 @@ internal class PostHogFeatureFlagsTest { this.reuseAnonymousId = reuseAnonymousId this.cachePreferences = cachePreferences this.evaluationContexts = evaluationContexts - this.remoteConfig = remoteConfig if (beforeSend != null) { addBeforeSend(beforeSend) } diff --git a/posthog/src/test/java/com/posthog/PostHogIntegrationChangeTest.kt b/posthog/src/test/java/com/posthog/PostHogIntegrationChangeTest.kt index 041a7548e..258cc39ea 100644 --- a/posthog/src/test/java/com/posthog/PostHogIntegrationChangeTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogIntegrationChangeTest.kt @@ -26,9 +26,8 @@ internal class PostHogIntegrationChangeTest { @Suppress("DEPRECATION") private val client = PostHog.with( - PostHogConfig(PROJECT_TOKEN).apply { + PostHogConfig(PROJECT_TOKEN, "http://127.0.0.1:1").apply { cachePreferences = PostHogMemoryPreferences() - remoteConfig = false preloadFeatureFlags = false addBeforeSend { null } addIntegration(integration) diff --git a/posthog/src/test/java/com/posthog/PostHogIntegrationInstallFailureTest.kt b/posthog/src/test/java/com/posthog/PostHogIntegrationInstallFailureTest.kt index d5a3717b8..93dd6f259 100644 --- a/posthog/src/test/java/com/posthog/PostHogIntegrationInstallFailureTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogIntegrationInstallFailureTest.kt @@ -27,10 +27,9 @@ internal class PostHogIntegrationInstallFailureTest { @Suppress("DEPRECATION") client = PostHog.with( - PostHogConfig(PROJECT_TOKEN).apply { + PostHogConfig(PROJECT_TOKEN, "http://127.0.0.1:1").apply { logger = testLogger cachePreferences = PostHogMemoryPreferences() - remoteConfig = false preloadFeatureFlags = false addBeforeSend { null } addIntegration(broken) diff --git a/posthog/src/test/java/com/posthog/PostHogOkHttpInterceptorTest.kt b/posthog/src/test/java/com/posthog/PostHogOkHttpInterceptorTest.kt index 09467943a..d91a9b2c3 100644 --- a/posthog/src/test/java/com/posthog/PostHogOkHttpInterceptorTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogOkHttpInterceptorTest.kt @@ -194,7 +194,6 @@ internal class PostHogOkHttpInterceptorTest { this.replayStoragePrefix = tmpDir.newFolder().absolutePath this.cachePreferences = PostHogMemoryPreferences() this.preloadFeatureFlags = false - this.remoteConfig = false } val postHog = diff --git a/posthog/src/test/java/com/posthog/PostHogTest.kt b/posthog/src/test/java/com/posthog/PostHogTest.kt index 5ce8ff3be..22176defb 100644 --- a/posthog/src/test/java/com/posthog/PostHogTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogTest.kt @@ -90,11 +90,11 @@ internal class PostHogTest { optOut: Boolean = false, persistOptOut: Boolean = true, preloadFeatureFlags: Boolean = true, - reloadFeatureFlags: Boolean = true, + // setup always requests /config; skip it when the test doesn't preload flags + reloadFeatureFlags: Boolean = preloadFeatureFlags, sendFeatureFlagEvent: Boolean = true, reuseAnonymousId: Boolean = false, integration: PostHogIntegration? = null, - remoteConfig: Boolean = false, surveys: Boolean = false, cachePreferences: PostHogPreferences? = PostHogMemoryPreferences(), beforeSend: PostHogBeforeSend? = null, @@ -121,7 +121,6 @@ internal class PostHogTest { this.reuseAnonymousId = reuseAnonymousId this.cachePreferences = cachePreferences this.evaluationContexts = evaluationContexts - this.remoteConfig = remoteConfig this.surveys = surveys if (beforeSend != null) { addBeforeSend(beforeSend) @@ -368,22 +367,6 @@ internal class PostHogTest { sut.close() } - @Test - fun `preload feature flags if enabled`() { - val http = mockHttp() - val url = http.url("/") - - val sut = getSut(url.toString()) - - remoteConfigExecutor.shutdownAndAwaitTermination() - - val request = http.takeRequest() - assertEquals(1, http.requestCount) - assertEquals("/flags/?v=2", request.path) - - sut.close() - } - @Test fun `reload feature flags and call the callback`() { val http = mockHttp() @@ -409,7 +392,7 @@ internal class PostHogTest { val http = mockHttp() val url = http.url("/") - val sut = getSut(url.toString(), remoteConfig = true, preloadFeatureFlags = false) + val sut = getSut(url.toString(), preloadFeatureFlags = false, reloadFeatureFlags = true) remoteConfigExecutor.shutdownAndAwaitTermination() @@ -437,7 +420,7 @@ internal class PostHogTest { ) val url = http.url("/") - val sut = getSut(url.toString(), remoteConfig = true) + val sut = getSut(url.toString()) remoteConfigExecutor.shutdownAndAwaitTermination() @@ -469,7 +452,7 @@ internal class PostHogTest { ) val url = http.url("/") - val sut = getSut(url.toString(), remoteConfig = true) + val sut = getSut(url.toString()) remoteConfigExecutor.shutdownAndAwaitTermination() @@ -1665,7 +1648,7 @@ internal class PostHogTest { override fun onSurveysLoaded(surveys: List) {} } - val sut = getSut(url.toString(), remoteConfig = true, integration = surveysIntegration) + val sut = getSut(url.toString(), integration = surveysIntegration) http.takeRequest() http.takeRequest() @@ -1690,7 +1673,7 @@ internal class PostHogTest { val url = http.url("/") val integration = FakePostHogIntegration() - val sut = getSut(url.toString(), remoteConfig = true, integration = integration) + val sut = getSut(url.toString(), integration = integration) remoteConfigExecutor.shutdownAndAwaitTermination() assertEquals(1, integration.remoteConfigCount) @@ -3207,17 +3190,25 @@ internal class PostHogTest { @Test fun `reset reloads flags as anon user`() { - val http = mockHttp() + // setup's /config reports no flags, so the only /flags request comes from reset + val http = + mockHttp( + response = + MockResponse() + .setBody(File("src/test/resources/json/basic-remote-config-no-flags.json").readText()), + ) + http.enqueue(MockResponse().setBody(responseFlagsApi)) val url = http.url("/") - val sut = getSut(url.toString(), preloadFeatureFlags = false) + val sut = getSut(url.toString(), preloadFeatureFlags = false, reloadFeatureFlags = true) sut.reset() remoteConfigExecutor.shutdownAndAwaitTermination() + assertEquals("/array/${PROJECT_TOKEN}/config", http.takeRequest().path) val request = http.takeRequest() - assertEquals(1, http.requestCount) + assertEquals(2, http.requestCount) assertEquals("/flags/?v=2", request.path) sut.close() @@ -4465,8 +4456,9 @@ internal class PostHogTest { mockHttp( response = MockResponse() - .setBody(responseFlagsApi), + .setBody(File("src/test/resources/json/basic-remote-config.json").readText()), ) + http.enqueue(MockResponse().setBody(responseFlagsApi)) val url = http.url("/") val sut = getSut(url.toString(), preloadFeatureFlags = true, context = TestPostHogContext())