diff --git a/.changeset/android-v4-major.md b/.changeset/android-v4-major.md new file mode 100644 index 000000000..e89b373a7 --- /dev/null +++ b/.changeset/android-v4-major.md @@ -0,0 +1,13 @@ +--- +'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 +- **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/api/posthog-android.api b/posthog-android/api/posthog-android.api index 71c9f8c4a..1749b4128 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; @@ -76,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 { @@ -130,12 +121,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 +137,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 @@ -161,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/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/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/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/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/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-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/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-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 77e664f61..d515cea20 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;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 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,12 +192,10 @@ 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; 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; @@ -229,7 +223,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,11 +242,9 @@ 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 - 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 @@ -272,8 +263,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 +384,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 +428,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 +461,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; @@ -622,11 +604,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; @@ -843,10 +820,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; @@ -979,7 +952,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; @@ -1002,7 +974,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; @@ -1016,13 +987,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 @@ -1167,12 +1131,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; @@ -1258,12 +1216,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; @@ -1276,8 +1228,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 { @@ -1298,14 +1248,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 @@ -1398,10 +1340,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 } @@ -1598,7 +1536,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/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index 281503ecb..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. @@ -2100,18 +2094,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 +2730,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..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 @@ -173,12 +164,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 +431,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 +573,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 +596,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/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/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/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? { 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/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..15122e4fe 100644 --- a/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogFeatureFlagsTest.kt @@ -48,13 +48,12 @@ 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(), - propertiesSanitizer: PostHogPropertiesSanitizer? = null, beforeSend: PostHogBeforeSend? = null, evaluationContexts: List? = null, context: PostHogContext? = null, @@ -73,9 +72,7 @@ internal class PostHogFeatureFlagsTest { this.sendFeatureFlagEvent = sendFeatureFlagEvent this.reuseAnonymousId = reuseAnonymousId this.cachePreferences = cachePreferences - this.propertiesSanitizer = propertiesSanitizer 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/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..22176defb 100644 --- a/posthog/src/test/java/com/posthog/PostHogTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogTest.kt @@ -90,14 +90,13 @@ 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(), - propertiesSanitizer: PostHogPropertiesSanitizer? = null, beforeSend: PostHogBeforeSend? = null, evaluationContexts: List? = null, context: PostHogContext? = null, @@ -121,9 +120,7 @@ internal class PostHogTest { this.sendFeatureFlagEvent = sendFeatureFlagEvent this.reuseAnonymousId = reuseAnonymousId this.cachePreferences = cachePreferences - this.propertiesSanitizer = propertiesSanitizer this.evaluationContexts = evaluationContexts - this.remoteConfig = remoteConfig this.surveys = surveys if (beforeSend != null) { addBeforeSend(beforeSend) @@ -370,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() @@ -411,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() @@ -439,7 +420,7 @@ internal class PostHogTest { ) val url = http.url("/") - val sut = getSut(url.toString(), remoteConfig = true) + val sut = getSut(url.toString()) remoteConfigExecutor.shutdownAndAwaitTermination() @@ -471,7 +452,7 @@ internal class PostHogTest { ) val url = http.url("/") - val sut = getSut(url.toString(), remoteConfig = true) + val sut = getSut(url.toString()) remoteConfigExecutor.shutdownAndAwaitTermination() @@ -1009,8 +990,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 +1005,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() } @@ -1668,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() @@ -1693,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) @@ -3068,48 +3048,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() @@ -3252,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() @@ -4510,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()) 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) + } + } } 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?,