diff --git a/.changeset/project-token-rename.md b/.changeset/project-token-rename.md new file mode 100644 index 000000000..79f75f2bc --- /dev/null +++ b/.changeset/project-token-rename.md @@ -0,0 +1,8 @@ +--- +'posthog': minor +'posthog-android': minor +'posthog-server': minor +'posthog-android-surveys-compose': patch +--- + +Add `projectToken` to `PostHogConfig` and `PostHogAndroidConfig` and deprecate `apiKey` (Kotlin subclasses calling `super(apiKey = ...)` must switch to `projectToken =`) diff --git a/posthog-android-surveys-compose/src/main/java/com/posthog/android/surveys/compose/PostHogSurveysComposeDelegate.kt b/posthog-android-surveys-compose/src/main/java/com/posthog/android/surveys/compose/PostHogSurveysComposeDelegate.kt index 24e0c2b66..e2718c072 100644 --- a/posthog-android-surveys-compose/src/main/java/com/posthog/android/surveys/compose/PostHogSurveysComposeDelegate.kt +++ b/posthog-android-surveys-compose/src/main/java/com/posthog/android/surveys/compose/PostHogSurveysComposeDelegate.kt @@ -29,7 +29,7 @@ import com.posthog.surveys.PostHogSurveysResumeAwareDelegate * implementation("com.posthog:posthog-android-surveys-compose:") * * // app init - * val config = PostHogAndroidConfig(apiKey).apply { surveys = true } + * val config = PostHogAndroidConfig(projectToken).apply { surveys = true } * PostHogAndroid.setup(applicationContext, config) * ``` * diff --git a/posthog-android/CONTRIBUTING.md b/posthog-android/CONTRIBUTING.md index caecd3a7d..f8d406502 100644 --- a/posthog-android/CONTRIBUTING.md +++ b/posthog-android/CONTRIBUTING.md @@ -14,7 +14,7 @@ unit tests covering your change, this will make your change much more likely to 2. Follow instructions here for setup https://developer.android.com/studio/run/emulator 3. Make sure you have Java 17 installed locally - `brew install openjdk@17` 4. Change the JDK in Android Studio to build using the correct version. -5. In the `posthog-android-sample` project, edit `MyApp.kt` and set the `apiKey` variable to your own PostHog Project API Key. +5. In the `posthog-android-sample` project, edit `MyApp.kt` and set the `projectToken` variable to your own PostHog project token. 6. Select `posthog-android-sample` and the device on the top bar and click run Above all, thank you for contributing! diff --git a/posthog-android/api/posthog-android.api b/posthog-android/api/posthog-android.api index 0dd4e7429..71c9f8c4a 100644 --- a/posthog-android/api/posthog-android.api +++ b/posthog-android/api/posthog-android.api @@ -17,6 +17,7 @@ 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 @@ -46,6 +47,11 @@ 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; 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 c81c3f4f6..3d188f772 100644 --- a/posthog-android/src/main/java/com/posthog/android/PostHogAndroidConfig.kt +++ b/posthog-android/src/main/java/com/posthog/android/PostHogAndroidConfig.kt @@ -10,7 +10,10 @@ import com.posthog.internal.PostHogQueue /** * Android SDK configuration. * - * @param apiKey PostHog project API key. Leading and trailing whitespace is trimmed. + * @param projectToken Your PostHog project token, which starts with `phc_`. Required, no default. + * Find it in your PostHog project settings: https://us.posthog.com/settings/project-details#variables. + * Leading and trailing whitespace is trimmed. Formerly named `apiKey`. Example: + * `PostHogAndroidConfig(projectToken = "phc_your_project_token")`. * @param host PostHog ingestion host. Defaults to [DEFAULT_HOST]. * @property captureApplicationLifecycleEvents Whether to capture application lifecycle events * automatically, including app installed, app updated, app opened, and app backgrounded. @@ -42,7 +45,7 @@ import com.posthog.internal.PostHogQueue public open class PostHogAndroidConfig @JvmOverloads constructor( - apiKey: String, + projectToken: String, host: String = DEFAULT_HOST, public var captureApplicationLifecycleEvents: Boolean = true, public var captureDeepLinks: Boolean = true, @@ -52,7 +55,7 @@ public open class PostHogAndroidConfig public var capturePushNotificationSubscriptions: Boolean = true, public var capturePushNotificationOpened: Boolean = true, ) : PostHogConfig( - apiKey = apiKey, + projectToken = projectToken, host = host, queueProvider = { config, api, endpoint, storagePrefix, executor -> val spec = @@ -93,4 +96,40 @@ 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/PostHogSharedPreferences.kt b/posthog-android/src/main/java/com/posthog/android/internal/PostHogSharedPreferences.kt index b98a7dba3..9a0945472 100644 --- a/posthog-android/src/main/java/com/posthog/android/internal/PostHogSharedPreferences.kt +++ b/posthog-android/src/main/java/com/posthog/android/internal/PostHogSharedPreferences.kt @@ -49,7 +49,7 @@ internal class PostHogSharedPreferences( sharedPreferences?.let { return it } val prefs = try { - context.getSharedPreferences("posthog-android-${config.apiKey}", MODE_PRIVATE) + context.getSharedPreferences("posthog-android-${config.projectToken}", MODE_PRIVATE) } catch (e: IllegalStateException) { config.logger.log("Shared preferences are not available until the device is unlocked (Direct Boot): $e.") return null diff --git a/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayQueue.kt b/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayQueue.kt index af23a918a..39c58d1aa 100644 --- a/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayQueue.kt +++ b/posthog-android/src/main/java/com/posthog/android/replay/PostHogReplayQueue.kt @@ -22,15 +22,15 @@ internal class PostHogReplayQueue internal constructor( replayStoragePrefix: String?, private val executor: ExecutorService, ) : PostHogQueueInterface { - private val replayDir = replayStoragePrefix?.let { File(it, config.apiKey) } + private val replayDir = replayStoragePrefix?.let { File(it, config.projectToken) } private val bufferQueue: PostHogReplayBufferQueue = PostHogReplayBufferQueue( config, if (replayStoragePrefix != null) { - File("$replayStoragePrefix-buffer", config.apiKey) + File("$replayStoragePrefix-buffer", config.projectToken) } else { - File(System.getProperty("java.io.tmpdir"), "posthog-replay-buffer/${config.apiKey}") + File(System.getProperty("java.io.tmpdir"), "posthog-replay-buffer/${config.projectToken}") }, ) 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 d92b5fb54..627c29942 100644 --- a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidConfigTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidConfigTest.kt @@ -10,15 +10,27 @@ internal class PostHogAndroidConfigTest { private val config = PostHogAndroidConfig(API_KEY) @Test - fun `captureApplicationLifecycleEvents sets given apiKey`() { - assertEquals(API_KEY, config.apiKey) + fun `captureApplicationLifecycleEvents sets given projectToken`() { + assertEquals(API_KEY, config.projectToken) } @Test fun `trims whitespace-sensitive config values`() { val config = PostHogAndroidConfig(" \n$API_KEY\t ") + assertEquals(API_KEY, config.projectToken) + } + + @Suppress("DEPRECATION") + @Test + fun `deprecated apiKey factory and getter alias projectToken`() { + val config = PostHogAndroidConfig(apiKey = " $API_KEY ", captureDeepLinks = false, capturePushNotificationOpened = false) + + assertEquals(API_KEY, config.projectToken) assertEquals(API_KEY, config.apiKey) + assertFalse(config.captureDeepLinks) + assertFalse(config.capturePushNotificationOpened) + assertTrue(config.captureScreenViews) } @Test diff --git a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidTest.kt b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidTest.kt index 67c220a2a..26d8a57f2 100644 --- a/posthog-android/src/test/java/com/posthog/android/PostHogAndroidTest.kt +++ b/posthog-android/src/test/java/com/posthog/android/PostHogAndroidTest.kt @@ -64,7 +64,7 @@ internal class PostHogAndroidTest { PostHogAndroid.setup(context, config) assertTrue(PostHog.isOptOut()) - assertTrue(logger.messages.any { it.contains("PostHog SDK is disabled because the API key is required") }) + assertTrue(logger.messages.any { it.contains("PostHog SDK is disabled because the project token is required") }) } @Test diff --git a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt index dab52c17a..5b328a9ce 100644 --- a/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt +++ b/posthog-samples/posthog-android-sample/src/main/java/com/posthog/android/sample/MyApp.kt @@ -13,19 +13,19 @@ class MyApp : Application() { enableStrictMode() // Demo: -// val apiKey = "_6SG-F7I1vCuZ-HdJL3VZQqjBlaSb1_20hDPwqMNnGI" +// val projectToken = "_6SG-F7I1vCuZ-HdJL3VZQqjBlaSb1_20hDPwqMNnGI" // ManoelTesting: - val apiKey = "phc_WKfvDfedaJEDCoUmt9pVa3OWtbbUP1W2ctxwXkt3A3n" + val projectToken = "phc_WKfvDfedaJEDCoUmt9pVa3OWtbbUP1W2ctxwXkt3A3n" // PaulKey -// val apiKey = "phc_GavhjwMwc75N4HsaLjMTEvH8Kpsz70rZ3N0E9ho89YJ" -// val config = PostHogAndroidConfig(apiKey, host = "https://3727-86-27-112-156.ngrok-free.app").apply { +// val projectToken = "phc_GavhjwMwc75N4HsaLjMTEvH8Kpsz70rZ3N0E9ho89YJ" +// val config = PostHogAndroidConfig(projectToken, host = "https://3727-86-27-112-156.ngrok-free.app").apply { val mockHost = BuildConfig.POSTHOG_HOST val config = ( if (mockHost.isNotEmpty()) { PostHogAndroidConfig("phc_MOCK", host = mockHost) } else { - PostHogAndroidConfig(apiKey) + PostHogAndroidConfig(projectToken) } ).apply { debug = true diff --git a/posthog-server/api/posthog-server.api b/posthog-server/api/posthog-server.api index 0f4c373ab..34c82a6f8 100644 --- a/posthog-server/api/posthog-server.api +++ b/posthog-server/api/posthog-server.api @@ -154,6 +154,7 @@ public class com/posthog/server/PostHogConfig { public final fun getPersonalApiKey ()Ljava/lang/String; public final fun getPollIntervalSeconds ()I public final fun getPreloadFeatureFlags ()Z + public final fun getProjectToken ()Ljava/lang/String; public final fun getProxy ()Ljava/net/Proxy; public final fun getReleaseIdentifier ()Ljava/lang/String; public final fun getRemoteConfig ()Z @@ -215,6 +216,8 @@ public final class com/posthog/server/PostHogConfig$Builder { public final class com/posthog/server/PostHogConfig$Companion { public final fun builder (Ljava/lang/String;)Lcom/posthog/server/PostHogConfig$Builder; + public final synthetic fun invoke (Ljava/lang/String;Ljava/lang/String;ZZZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;Ljava/net/Proxy;IIIZLjava/lang/String;ILjava/util/List;)Lcom/posthog/server/PostHogConfig; + public static synthetic fun invoke$default (Lcom/posthog/server/PostHogConfig$Companion;Ljava/lang/String;Ljava/lang/String;ZZZZIIIILcom/posthog/PostHogEncryption;Lcom/posthog/PostHogOnFeatureFlags;Ljava/net/Proxy;IIIZLjava/lang/String;ILjava/util/List;ILjava/lang/Object;)Lcom/posthog/server/PostHogConfig; } public final class com/posthog/server/PostHogEvaluateFlagsOptions { 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 6416dc87c..f224c892d 100644 --- a/posthog-server/src/main/java/com/posthog/server/PostHogConfig.kt +++ b/posthog-server/src/main/java/com/posthog/server/PostHogConfig.kt @@ -19,9 +19,17 @@ import java.util.Collections */ public open class PostHogConfig constructor( /** - * The PostHog project API key. + * Your PostHog project token, which starts with `phc_`. Required, no default. + * + * Find it in your PostHog project settings: + * https://us.posthog.com/settings/project-details#variables. Leading and trailing whitespace + * is trimmed. Formerly named `apiKey`. + * + * ```kotlin + * val config = PostHogConfig(projectToken = "phc_your_project_token") + * ``` */ - apiKey: String, + projectToken: String, /** * The PostHog Host * Defaults to https://us.i.posthog.com @@ -142,9 +150,31 @@ public open class PostHogConfig constructor( public var evaluationContexts: List? = null, ) { /** - * The PostHog project API key, trimmed of leading and trailing whitespace. + * Your PostHog project token, trimmed of leading and trailing whitespace. + * + * Find it in your PostHog project settings: + * https://us.posthog.com/settings/project-details#variables. Set it through the constructor or + * [builder]; there is no default. + * + * ```kotlin + * val token = PostHogConfig(projectToken = "phc_your_project_token").projectToken + * ``` */ - public val apiKey: String = apiKey.trim() + 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 @@ -269,7 +299,7 @@ public open class PostHogConfig constructor( internal fun asCoreConfig(): com.posthog.PostHogConfig { val coreConfig = com.posthog.PostHogConfig( - apiKey = apiKey, + projectToken = projectToken, host = host, debug = debug, sendFeatureFlagEvent = sendFeatureFlagEvent, @@ -378,17 +408,79 @@ public open class PostHogConfig constructor( /** * Creates a Java-friendly builder. * - * @param apiKey PostHog project API key. + * @param apiKey Your PostHog project token, which starts with `phc_`. Find it in your + * PostHog project settings: https://us.posthog.com/settings/project-details#variables. + * Example: `PostHogConfig.builder("phc_your_project_token").build()`. * @return A new [Builder]. */ @JvmStatic public fun builder(apiKey: String): Builder = Builder(apiKey) + + /** + * 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, + sendFeatureFlagEvent: Boolean = true, + preloadFeatureFlags: Boolean = true, + remoteConfig: Boolean = true, + flushAt: Int = DEFAULT_FLUSH_AT, + maxQueueSize: Int = DEFAULT_MAX_QUEUE_SIZE, + maxBatchSize: Int = DEFAULT_MAX_BATCH_SIZE, + flushIntervalSeconds: Int = DEFAULT_FLUSH_INTERVAL_SECONDS, + encryption: PostHogEncryption? = null, + onFeatureFlags: PostHogOnFeatureFlags? = null, + proxy: Proxy? = null, + featureFlagCacheSize: Int = DEFAULT_FEATURE_FLAG_CACHE_SIZE, + featureFlagCacheMaxAgeMs: Int = DEFAULT_FEATURE_FLAG_CACHE_MAX_AGE_MS, + featureFlagCalledCacheSize: Int = DEFAULT_FEATURE_FLAG_CALLED_CACHE_SIZE, + localEvaluation: Boolean = false, + personalApiKey: String? = null, + pollIntervalSeconds: Int = DEFAULT_POLL_INTERVAL_SECONDS, + evaluationContexts: List? = null, + ): PostHogConfig = + PostHogConfig( + projectToken = apiKey, + host = host, + debug = debug, + sendFeatureFlagEvent = sendFeatureFlagEvent, + preloadFeatureFlags = preloadFeatureFlags, + remoteConfig = remoteConfig, + flushAt = flushAt, + maxQueueSize = maxQueueSize, + maxBatchSize = maxBatchSize, + flushIntervalSeconds = flushIntervalSeconds, + encryption = encryption, + onFeatureFlags = onFeatureFlags, + proxy = proxy, + featureFlagCacheSize = featureFlagCacheSize, + featureFlagCacheMaxAgeMs = featureFlagCacheMaxAgeMs, + featureFlagCalledCacheSize = featureFlagCalledCacheSize, + localEvaluation = localEvaluation, + personalApiKey = personalApiKey, + pollIntervalSeconds = pollIntervalSeconds, + evaluationContexts = evaluationContexts, + ) } /** * Java-friendly builder for [PostHogConfig]. * - * @param apiKey PostHog project API key. + * @param apiKey Your PostHog project token, which starts with `phc_`. Find it in your PostHog + * project settings: https://us.posthog.com/settings/project-details#variables. */ public class Builder(private val apiKey: String) { private var host: String = DEFAULT_HOST @@ -647,7 +739,7 @@ public open class PostHogConfig constructor( public fun build(): PostHogConfig { val config = PostHogConfig( - apiKey = apiKey, + projectToken = apiKey, host = host, debug = debug, sendFeatureFlagEvent = sendFeatureFlagEvent, 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 2fd7d977b..c5ea38887 100644 --- a/posthog-server/src/test/java/com/posthog/server/PostHogConfigTest.kt +++ b/posthog-server/src/test/java/com/posthog/server/PostHogConfigTest.kt @@ -13,9 +13,9 @@ import kotlin.test.assertTrue internal class PostHogConfigTest { @Test fun `constructor sets all required parameters with defaults`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) - assertEquals(TEST_API_KEY, config.apiKey) + assertEquals(TEST_API_KEY, config.projectToken) assertEquals(PostHogConfig.DEFAULT_HOST, config.host) assertEquals(false, config.debug) assertEquals(true, config.sendFeatureFlagEvent) @@ -37,26 +37,36 @@ internal class PostHogConfigTest { fun `trims whitespace-sensitive config values`() { val config = PostHogConfig( - apiKey = " \n$TEST_API_KEY\t ", + projectToken = " \n$TEST_API_KEY\t ", host = " \nhttps://eu.i.posthog.com/\t ", personalApiKey = " \nphx_test_personal_api_key\t ", ) - assertEquals(TEST_API_KEY, config.apiKey) + assertEquals(TEST_API_KEY, config.projectToken) assertEquals("https://eu.i.posthog.com/", config.host) assertEquals("phx_test_personal_api_key", config.personalApiKey) } + @Test + fun `deprecated apiKey factory and getter alias projectToken`() { + val config = PostHogConfig(apiKey = " $TEST_API_KEY ", personalApiKey = "phx_test", evaluationContexts = listOf("web")) + + assertEquals(TEST_API_KEY, config.projectToken) + assertEquals(TEST_API_KEY, config.apiKey) + assertEquals("phx_test", config.personalApiKey) + assertEquals(listOf("web"), config.evaluationContexts) + } + @Test fun `defaults blank personal api key to null after trimming whitespace`() { - val config = PostHogConfig(apiKey = TEST_API_KEY, personalApiKey = " \n\t ") + val config = PostHogConfig(projectToken = TEST_API_KEY, personalApiKey = " \n\t ") assertNull(config.personalApiKey) } @Test fun `defaults a blank host after trimming whitespace`() { - val config = PostHogConfig(apiKey = TEST_API_KEY, host = " \n\t ") + val config = PostHogConfig(projectToken = TEST_API_KEY, host = " \n\t ") assertEquals(PostHogConfig.DEFAULT_HOST, config.host) } @@ -69,7 +79,7 @@ internal class PostHogConfigTest { val config = PostHogConfig( - apiKey = "custom-api-key", + projectToken = "custom-api-key", host = "https://custom.host.com", debug = true, sendFeatureFlagEvent = false, @@ -86,7 +96,7 @@ internal class PostHogConfigTest { featureFlagCacheMaxAgeMs = 600000, ) - assertEquals("custom-api-key", config.apiKey) + assertEquals("custom-api-key", config.projectToken) assertEquals("https://custom.host.com", config.host) assertEquals(true, config.debug) assertEquals(false, config.sendFeatureFlagEvent) @@ -105,7 +115,7 @@ internal class PostHogConfigTest { @Test fun `addBeforeSend adds callback to internal list`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val beforeSend = createMockBeforeSend() config.addBeforeSend(beforeSend) @@ -115,7 +125,7 @@ internal class PostHogConfigTest { @Test fun `removeBeforeSend removes callback from internal list`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val beforeSend = createMockBeforeSend() config.addBeforeSend(beforeSend) @@ -127,7 +137,7 @@ internal class PostHogConfigTest { @Test fun `addIntegration adds integration to internal list`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val integration = createMockIntegration() config.addIntegration(integration) @@ -142,7 +152,7 @@ internal class PostHogConfigTest { val config = PostHogConfig( - apiKey = "test-key", + projectToken = "test-key", host = "https://test.host.com", debug = true, sendFeatureFlagEvent = false, @@ -160,7 +170,7 @@ internal class PostHogConfigTest { val coreConfig = config.asCoreConfig() - assertEquals("test-key", coreConfig.apiKey) + assertEquals("test-key", coreConfig.projectToken) assertEquals("https://test.host.com", coreConfig.host) assertEquals(true, coreConfig.debug) assertEquals(false, coreConfig.sendFeatureFlagEvent) @@ -176,7 +186,7 @@ internal class PostHogConfigTest { @Test fun `asCoreConfig propagates releaseIdentifier to core config`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) config.releaseIdentifier = "release-123" val coreConfig = config.asCoreConfig() @@ -186,7 +196,7 @@ internal class PostHogConfigTest { @Test fun `asCoreConfig applies beforeSend callbacks to core config`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val beforeSend1 = createMockBeforeSend() val beforeSend2 = createMockBeforeSend() @@ -198,7 +208,7 @@ internal class PostHogConfigTest { @Test fun `asCoreConfig applies integrations to core config`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val integration1 = createMockIntegration() val integration2 = createMockIntegration() @@ -228,7 +238,7 @@ internal class PostHogConfigTest { val builder = PostHogConfig.builder("test-api-key") val config = builder.build() - assertEquals("test-api-key", config.apiKey) + assertEquals("test-api-key", config.projectToken) } // Builder tests @@ -236,7 +246,7 @@ internal class PostHogConfigTest { fun `builder creates config with default values`() { val config = PostHogConfig.builder(TEST_API_KEY).build() - assertEquals(TEST_API_KEY, config.apiKey) + assertEquals(TEST_API_KEY, config.projectToken) assertEquals(PostHogConfig.DEFAULT_HOST, config.host) assertEquals(false, config.debug) assertEquals(true, config.sendFeatureFlagEvent) @@ -408,7 +418,7 @@ internal class PostHogConfigTest { .featureFlagCalledCacheSize(30) .build() - assertEquals(TEST_API_KEY, config.apiKey) + assertEquals(TEST_API_KEY, config.projectToken) assertEquals("https://custom.host.com", config.host) assertEquals(true, config.debug) assertEquals(false, config.sendFeatureFlagEvent) @@ -442,7 +452,7 @@ internal class PostHogConfigTest { @Test fun `config properties are mutable after creation`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) // Test that properties can be modified config.debug = true @@ -473,7 +483,7 @@ internal class PostHogConfigTest { @Test fun `multiple beforeSend callbacks can be added and removed`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val beforeSend1 = createMockBeforeSend() val beforeSend2 = createMockBeforeSend() val beforeSend3 = createMockBeforeSend() @@ -491,7 +501,7 @@ internal class PostHogConfigTest { @Test fun `multiple integrations can be added`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val integration1 = createMockIntegration() val integration2 = createMockIntegration() @@ -503,7 +513,7 @@ internal class PostHogConfigTest { @Test fun `asCoreConfig creates new instance each time`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val coreConfig1 = config.asCoreConfig() val coreConfig2 = config.asCoreConfig() @@ -511,7 +521,7 @@ internal class PostHogConfigTest { // Should be different instances assertNotEquals(coreConfig1, coreConfig2) // But should have same properties - assertEquals(coreConfig1.apiKey, coreConfig2.apiKey) + assertEquals(coreConfig1.projectToken, coreConfig2.projectToken) assertEquals(coreConfig1.host, coreConfig2.host) } @@ -575,28 +585,28 @@ internal class PostHogConfigTest { @Test fun `constructor sets evaluationContexts to null by default`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) assertNull(config.evaluationContexts) } @Test fun `constructor accepts evaluationContexts parameter`() { val contexts = listOf("web", "mobile", "checkout") - val config = PostHogConfig(apiKey = TEST_API_KEY, evaluationContexts = contexts) + val config = PostHogConfig(projectToken = TEST_API_KEY, evaluationContexts = contexts) assertEquals(contexts, config.evaluationContexts) } @Test fun `asCoreConfig propagates evaluationContexts to core config`() { val contexts = listOf("web", "mobile") - val config = PostHogConfig(apiKey = TEST_API_KEY, evaluationContexts = contexts) + val config = PostHogConfig(projectToken = TEST_API_KEY, evaluationContexts = contexts) val coreConfig = config.asCoreConfig() assertEquals(contexts, coreConfig.evaluationContexts) } @Test fun `asCoreConfig propagates null evaluationContexts`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val coreConfig = config.asCoreConfig() assertNull(coreConfig.evaluationContexts) } @@ -623,14 +633,14 @@ internal class PostHogConfigTest { @Test fun `constructor sets flagDefinitionCacheProvider to null by default`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) assertNull(config.flagDefinitionCacheProvider) } @Test fun `flagDefinitionCacheProvider property accepts provider`() { val provider = NoOpFlagDefinitionCacheProvider() - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) config.flagDefinitionCacheProvider = provider assertEquals(provider, config.flagDefinitionCacheProvider) } @@ -650,7 +660,7 @@ internal class PostHogConfigTest { @Test fun `in-app classification properties have expected defaults`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) assertTrue(config.inAppIncludes.isEmpty()) assertEquals(PostHogConfig.DEFAULT_IN_APP_EXCLUDES, config.inAppExcludes) @@ -684,7 +694,7 @@ internal class PostHogConfigTest { @Test fun `asCoreConfig propagates in-app classification settings to core config`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) config.inAppIncludes = listOf("com.myapp.") config.inAppExcludes = listOf("com.thirdparty.") @@ -696,7 +706,7 @@ internal class PostHogConfigTest { @Test fun `asCoreConfig propagates default excludes`() { - val config = PostHogConfig(apiKey = TEST_API_KEY) + val config = PostHogConfig(projectToken = TEST_API_KEY) val coreConfig = config.asCoreConfig() @@ -748,7 +758,7 @@ internal class PostHogConfigTest { assertEquals("java.", PostHogConfig.DEFAULT_IN_APP_EXCLUDES[0]) assertEquals( PostHogConfig.DEFAULT_IN_APP_EXCLUDES, - PostHogConfig(apiKey = TEST_API_KEY).inAppExcludes, + PostHogConfig(projectToken = TEST_API_KEY).inAppExcludes, "A fresh config must still see the untouched defaults", ) } diff --git a/posthog-server/src/test/java/com/posthog/server/Utils.kt b/posthog-server/src/test/java/com/posthog/server/Utils.kt index 9a992b92c..db25884a2 100644 --- a/posthog-server/src/test/java/com/posthog/server/Utils.kt +++ b/posthog-server/src/test/java/com/posthog/server/Utils.kt @@ -224,7 +224,7 @@ public fun createTestConfig( ): PostHogConfig { val config = PostHogConfig( - apiKey = apiKey, + projectToken = apiKey, host = host, ) config.logger = logger diff --git a/posthog-server/src/test/java/com/posthog/server/internal/FlagEvaluatorTest.kt b/posthog-server/src/test/java/com/posthog/server/internal/FlagEvaluatorTest.kt index 66efe327a..a957a8239 100644 --- a/posthog-server/src/test/java/com/posthog/server/internal/FlagEvaluatorTest.kt +++ b/posthog-server/src/test/java/com/posthog/server/internal/FlagEvaluatorTest.kt @@ -21,7 +21,7 @@ internal class FlagEvaluatorTest { @Before internal fun setUp() { - config = PostHogConfig(apiKey = "test-key") + config = PostHogConfig(projectToken = "test-key") evaluator = FlagEvaluator(config) } diff --git a/posthog/api/posthog.api b/posthog/api/posthog.api index 71d451629..aed293d68 100644 --- a/posthog/api/posthog.api +++ b/posthog/api/posthog.api @@ -195,6 +195,7 @@ public class com/posthog/PostHogConfig { public final fun getPersistOptOut ()Z 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; @@ -271,6 +272,8 @@ 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 { diff --git a/posthog/src/main/java/com/posthog/PostHog.kt b/posthog/src/main/java/com/posthog/PostHog.kt index b74829757..ce5f48ec7 100644 --- a/posthog/src/main/java/com/posthog/PostHog.kt +++ b/posthog/src/main/java/com/posthog/PostHog.kt @@ -181,13 +181,15 @@ public class PostHog private constructor( config.logger = if (config.logger is PostHogNoOpLogger) PostHogPrintLogger(config) else config.logger - if (config.apiKey.isEmpty()) { - config.logger.log("PostHog SDK is disabled because the API key is required and was empty after trimming whitespace.") + if (config.projectToken.isEmpty()) { + config.logger.log( + "PostHog SDK is disabled because the project token is required and was empty after trimming whitespace.", + ) return } - if (!apiKeys.add(config.apiKey)) { - config.logger.log("API Key: ${config.apiKey} already has a PostHog instance.") + if (!apiKeys.add(config.projectToken)) { + config.logger.log("Project token: ${config.projectToken} already has a PostHog instance.") } val cachePreferences = config.cachePreferences ?: memoryPreferences @@ -426,7 +428,7 @@ public class PostHog private constructor( config: PostHogConfig, serializer: PostHogSerializer, ) { - val cachedPrefs = getPreferences().getValue(config.apiKey) as? String + val cachedPrefs = getPreferences().getValue(config.projectToken) as? String cachedPrefs?.let { try { serializer.deserialize?>(it.reader())?.let { props -> @@ -440,7 +442,7 @@ public class PostHog private constructor( this.distinctId = distinctId } - getPreferences().remove(config.apiKey) + getPreferences().remove(config.projectToken) } } catch (e: Throwable) { config.logger.log("Legacy cached prefs: $cachedPrefs failed to parse: $e.") @@ -544,7 +546,7 @@ public class PostHog private constructor( enabled = false config?.let { config -> - apiKeys.remove(config.apiKey) + apiKeys.remove(config.projectToken) config.integrations.forEach { try { diff --git a/posthog/src/main/java/com/posthog/PostHogConfig.kt b/posthog/src/main/java/com/posthog/PostHogConfig.kt index 630dd885b..eb730adab 100644 --- a/posthog/src/main/java/com/posthog/PostHogConfig.kt +++ b/posthog/src/main/java/com/posthog/PostHogConfig.kt @@ -31,9 +31,18 @@ import java.util.concurrent.ExecutorService */ public open class PostHogConfig( /** - * The PostHog project API key. + * Your PostHog project token, which starts with `phc_`. Required, no default. + * + * Find it in your PostHog project settings: + * https://us.posthog.com/settings/project-details#variables. Leading and trailing whitespace + * is trimmed; when the result is blank, setup logs a message and the SDK stays disabled. + * Formerly named `apiKey`. + * + * ```kotlin + * val config = PostHogConfig(projectToken = "phc_your_project_token") + * ``` */ - apiKey: String, + projectToken: String, /** * The PostHog Host * Defaults to https://us.i.posthog.com @@ -255,26 +264,7 @@ public open class PostHogConfig( PostHogDefaultPersonPropertiesProvider?, PostHogOnRemoteConfigLoaded?, PostHogFeatureFlagCalledProvider?, - ) -> PostHogFeatureFlagsInterface = - { - config, - api, - executor, - defaultPersonPropertiesProvider, - onRemoteConfigLoaded, - featureFlagCalledProvider, - -> - PostHogRemoteConfig( - config, - api, - executor, - defaultPersonPropertiesProvider ?: PostHogDefaultPersonPropertiesProvider { - emptyMap() - }, - featureFlagCalledProvider ?: PostHogFeatureFlagCalledProvider { _, _ -> }, - onRemoteConfigLoaded, - ) - }, + ) -> PostHogFeatureFlagsInterface = DEFAULT_REMOTE_CONFIG_PROVIDER, /** * Factory to instantiate a custom queue implementation. */ @@ -285,15 +275,7 @@ public open class PostHogConfig( PostHogApiEndpoint, String?, ExecutorService, - ) -> PostHogQueueInterface = - { config, api, endpoint, storagePrefix, executor -> - val spec = - when (endpoint) { - PostHogApiEndpoint.BATCH -> EndpointSpec.batch(config, api, storagePrefix) - PostHogApiEndpoint.SNAPSHOT -> EndpointSpec.snapshot(config, api, storagePrefix) - } - PostHogQueue(config, spec, executor) - }, + ) -> PostHogQueueInterface = DEFAULT_QUEUE_PROVIDER, /** * Configuration for PostHog Error Tracking feature. */ @@ -372,7 +354,7 @@ public open class PostHogConfig( * including for hosts that merely wrap the SDK without owning consent. * * ```kotlin - * val config = PostHogAndroidConfig(apiKey = "").apply { + * val config = PostHogAndroidConfig(projectToken = "").apply { * persistOptOut = false * optOut = myConsentManager.isOptedOut * } @@ -452,9 +434,31 @@ public open class PostHogConfig( public var compression: PostHogCompression = PostHogCompression.GZIP /** - * The PostHog project API key, trimmed of leading and trailing whitespace. + * Your PostHog project token, trimmed of leading and trailing whitespace. + * + * Find it in your PostHog project settings: + * https://us.posthog.com/settings/project-details#variables. Set it through the constructor; + * there is no default. + * + * ```kotlin + * val token = PostHogConfig(projectToken = "phc_your_project_token").projectToken + * ``` + */ + public val projectToken: String = projectToken.trim() + + /** + * Deprecated alias for [projectToken]; returns the same trimmed value. + * + * ```kotlin + * val token = config.projectToken // instead of config.apiKey + * ``` */ - public val apiKey: String = apiKey.trim() + @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 @@ -635,5 +639,147 @@ 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, + ) } } + +private val DEFAULT_REMOTE_CONFIG_PROVIDER: ( + PostHogConfig, + PostHogApi, + ExecutorService, + PostHogDefaultPersonPropertiesProvider?, + PostHogOnRemoteConfigLoaded?, + PostHogFeatureFlagCalledProvider?, +) -> PostHogFeatureFlagsInterface = + { + config, + api, + executor, + defaultPersonPropertiesProvider, + onRemoteConfigLoaded, + featureFlagCalledProvider, + -> + PostHogRemoteConfig( + config, + api, + executor, + defaultPersonPropertiesProvider ?: PostHogDefaultPersonPropertiesProvider { + emptyMap() + }, + featureFlagCalledProvider ?: PostHogFeatureFlagCalledProvider { _, _ -> }, + onRemoteConfigLoaded, + ) + } + +private val DEFAULT_QUEUE_PROVIDER: ( + PostHogConfig, + PostHogApi, + PostHogApiEndpoint, + String?, + ExecutorService, +) -> PostHogQueueInterface = + { config, api, endpoint, storagePrefix, executor -> + val spec = + when (endpoint) { + PostHogApiEndpoint.BATCH -> EndpointSpec.batch(config, api, storagePrefix) + PostHogApiEndpoint.SNAPSHOT -> EndpointSpec.snapshot(config, api, storagePrefix) + } + PostHogQueue(config, spec, executor) + } diff --git a/posthog/src/main/java/com/posthog/PostHogStateless.kt b/posthog/src/main/java/com/posthog/PostHogStateless.kt index 4eb589176..385c6183d 100644 --- a/posthog/src/main/java/com/posthog/PostHogStateless.kt +++ b/posthog/src/main/java/com/posthog/PostHogStateless.kt @@ -58,13 +58,15 @@ public open class PostHogStateless protected constructor( config.logger = if (config.logger is PostHogNoOpLogger) PostHogPrintLogger(config) else config.logger - if (config.apiKey.isEmpty()) { - config.logger.log("PostHog SDK is disabled because the API key is required and was empty after trimming whitespace.") + if (config.projectToken.isEmpty()) { + config.logger.log( + "PostHog SDK is disabled because the project token is required and was empty after trimming whitespace.", + ) return } - if (!apiKeys.add(config.apiKey)) { - config.logger.log("API Key: ${config.apiKey} already has a PostHog instance.") + if (!apiKeys.add(config.projectToken)) { + config.logger.log("Project token: ${config.projectToken} already has a PostHog instance.") } config.cachePreferences = memoryPreferences @@ -123,7 +125,7 @@ public open class PostHogStateless protected constructor( enabled = false config?.let { config -> - apiKeys.remove(config.apiKey) + apiKeys.remove(config.projectToken) config.integrations.forEach { try { diff --git a/posthog/src/main/java/com/posthog/internal/PostHogApi.kt b/posthog/src/main/java/com/posthog/internal/PostHogApi.kt index 3394b6f6e..daa0f5bab 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogApi.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogApi.kt @@ -70,7 +70,7 @@ public class PostHogApi( @Throws(PostHogApiError::class, IOException::class) public fun batch(events: List) { - val batch = PostHogBatchEvent(config.apiKey, events) + val batch = PostHogBatchEvent(config.projectToken, events) val url = "$theHost/batch" val request = @@ -88,7 +88,7 @@ public class PostHogApi( @Throws(PostHogApiError::class, IOException::class) public fun snapshot(events: List) { events.forEach { - it.apiKey = config.apiKey + it.apiKey = config.projectToken } val url = "$theHost${config.snapshotEndpoint}" @@ -124,7 +124,7 @@ public class PostHogApi( sdkVersion = config.sdkVersion, ) - val url = "$theHost/i/v1/logs?token=${config.apiKey}" + val url = "$theHost/i/v1/logs?token=${config.projectToken}" val request = makeRequest(url) { logRequest(payload, url) @@ -160,7 +160,7 @@ public class PostHogApi( ) { val pushSubscription = PostHogPushSubscriptionRequest( - projectToken = config.apiKey, + projectToken = config.projectToken, distinctId = distinctId, deviceToken = deviceToken, appId = appId, @@ -258,7 +258,7 @@ public class PostHogApi( ): PostHogFlagsResponse? { val flagsRequest = PostHogFlagsRequest( - config.apiKey, + config.projectToken, distinctId, anonymousId = anonymousId, deviceId = deviceId, @@ -366,7 +366,7 @@ public class PostHogApi( val request = Request.Builder() - .url("$host/array/${config.apiKey}/config") + .url("$host/array/${config.projectToken}/config") .header("User-Agent", config.userAgent) .header("Content-Type", APP_JSON_UTF_8) .get() @@ -404,7 +404,7 @@ public class PostHogApi( personalApiKey: String, etag: String? = null, ): LocalEvaluationApiResponse { - val url = "$theHost/api/feature_flag/local_evaluation/?token=${config.apiKey}&send_cohorts" + val url = "$theHost/api/feature_flag/local_evaluation/?token=${config.projectToken}&send_cohorts" val requestBuilder = Request.Builder() diff --git a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt index fe4c1f727..dee7f0437 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogPushSubscriptionManager.kt @@ -104,12 +104,12 @@ internal class PostHogPushSubscriptionManager( val prefix = config.storagePrefix ?: return@lazy null // Must stay out of /: PostHogQueue scans that whole directory as // cached event files and would send the record as an empty event, then delete it. - File(File(File(prefix, "push"), config.apiKey), PENDING_FILE_NAME) + File(File(File(prefix, "push"), config.projectToken), PENDING_FILE_NAME) } private val pendingUnregisterFile: File? by lazy { val prefix = config.storagePrefix ?: return@lazy null - File(File(File(prefix, "push"), config.apiKey), PENDING_UNREGISTER_FILE_NAME) + File(File(File(prefix, "push"), config.projectToken), PENDING_UNREGISTER_FILE_NAME) } // Test seam: computed backoff seconds are multiplied by this to get the scheduled delay in @@ -453,8 +453,8 @@ internal class PostHogPushSubscriptionManager( // The project API key resolves to no project, so every send gets the same 401. Guarding // here and not at each entry point covers identify resends and app_id changes too. config.logger.log( - "Push subscription skipped: this project API key was rejected. " + - "Check the key passed to PostHog.setup().", + "Push subscription skipped: this project token was rejected. " + + "Check the projectToken passed to PostHog.setup().", ) return } @@ -591,8 +591,8 @@ internal class PostHogPushSubscriptionManager( // The key resolves to no project, so every later attempt gets the same answer. Without // this the device re-posts on every app open for the life of the install. config.logger.log( - "Push subscription rejected: the project API key is not valid. " + - "No further push registrations will be sent for this key.", + "Push subscription rejected: the project token is not valid. " + + "No further push registrations will be sent for this token.", ) markTokenRejected() haltForSession() @@ -756,16 +756,16 @@ internal class PostHogPushSubscriptionManager( } private fun isTokenRejected(): Boolean { - val rejectedAt = readRejections()[config.apiKey]?.toLongOrNull() ?: return false + val rejectedAt = readRejections()[config.projectToken]?.toLongOrNull() ?: return false if (config.dateProvider.currentTimeMillis() - rejectedAt < REJECTED_REPROBE_MILLIS) { return true } - writeRejections(readRejections() - config.apiKey) + writeRejections(readRejections() - config.projectToken) return false } private fun markTokenRejected() { - writeRejections(readRejections() + (config.apiKey to config.dateProvider.currentTimeMillis().toString())) + writeRejections(readRejections() + (config.projectToken to config.dateProvider.currentTimeMillis().toString())) } /** Verdicts per api key, not one slot. diff --git a/posthog/src/main/java/com/posthog/internal/PostHogQueue.kt b/posthog/src/main/java/com/posthog/internal/PostHogQueue.kt index ac102bb2b..d04b3fa99 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogQueue.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogQueue.kt @@ -55,11 +55,11 @@ public class PostHogQueue( get() = synchronized(dequeLock) { deque.size } public val queueDirectory: File? - get() = spec.storagePrefix?.let { File(it, config.apiKey) } + get() = spec.storagePrefix?.let { File(it, config.projectToken) } private fun addRecordSync(record: Record): Boolean { spec.storagePrefix?.let { - val dir = File(it, config.apiKey) + val dir = File(it, config.projectToken) if (!dirCreated) { dir.mkdirs() diff --git a/posthog/src/main/java/com/posthog/internal/PostHogSendCachedEventsIntegration.kt b/posthog/src/main/java/com/posthog/internal/PostHogSendCachedEventsIntegration.kt index 009b7368e..4924b1f52 100644 --- a/posthog/src/main/java/com/posthog/internal/PostHogSendCachedEventsIntegration.kt +++ b/posthog/src/main/java/com/posthog/internal/PostHogSendCachedEventsIntegration.kt @@ -55,7 +55,7 @@ internal class PostHogSendCachedEventsIntegration( private fun flushLegacyEvents() { config.legacyStoragePrefix?.let { val legacyDir = File(it) - val legacyFile = File(legacyDir, "${config.apiKey}.tmp") + val legacyFile = File(legacyDir, "${config.projectToken}.tmp") if (!legacyFile.existsSafely(config)) { return diff --git a/posthog/src/main/java/com/posthog/logs/PostHogLogsConfig.kt b/posthog/src/main/java/com/posthog/logs/PostHogLogsConfig.kt index ec990dbaf..b1b11658d 100644 --- a/posthog/src/main/java/com/posthog/logs/PostHogLogsConfig.kt +++ b/posthog/src/main/java/com/posthog/logs/PostHogLogsConfig.kt @@ -13,7 +13,7 @@ import java.util.concurrent.CopyOnWriteArrayList * ### Kotlin * * ```kotlin - * val config = PostHogAndroidConfig(apiKey).apply { + * val config = PostHogAndroidConfig(projectToken).apply { * logs.serviceName = "checkout-android" * logs.environment = "production" * logs.resourceAttributes = mapOf("region" to "us-east-1") @@ -33,7 +33,7 @@ import java.util.concurrent.CopyOnWriteArrayList * ### Java * * ```java - * PostHogAndroidConfig config = new PostHogAndroidConfig(apiKey); + * PostHogAndroidConfig config = new PostHogAndroidConfig(projectToken); * config.getLogs().setServiceName("checkout-android"); * config.getLogs().setEnvironment("production"); * config.getLogs().addBeforeSend(record -> diff --git a/posthog/src/test/java/com/posthog/PostHogConfigTest.kt b/posthog/src/test/java/com/posthog/PostHogConfigTest.kt index 86bd00efa..8ae8bcecb 100644 --- a/posthog/src/test/java/com/posthog/PostHogConfigTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogConfigTest.kt @@ -19,10 +19,21 @@ internal class PostHogConfigTest { fun `trims whitespace-sensitive config values`() { val config = PostHogConfig(" \n$API_KEY\t ", " \nhttps://eu.i.posthog.com/\t ") - assertEquals(API_KEY, config.apiKey) + assertEquals(API_KEY, config.projectToken) assertEquals("https://eu.i.posthog.com/", config.host) } + @Suppress("DEPRECATION") + @Test + fun `deprecated apiKey factory and getter alias projectToken`() { + val config = PostHogConfig(apiKey = " $API_KEY ", host = "https://eu.i.posthog.com", flushAt = 7) + + assertEquals(API_KEY, config.projectToken) + assertEquals(API_KEY, 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(API_KEY, " \n\t ") diff --git a/posthog/src/test/java/com/posthog/PostHogStatelessTest.kt b/posthog/src/test/java/com/posthog/PostHogStatelessTest.kt index 6a58aa54c..5922cdfa3 100644 --- a/posthog/src/test/java/com/posthog/PostHogStatelessTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogStatelessTest.kt @@ -277,7 +277,7 @@ internal class PostHogStatelessTest { sut.setup(config) assertFalse(sut.isEnabledPublic()) - assertTrue(mockLogger.messages.any { it.contains("PostHog SDK is disabled because the API key is required") }) + assertTrue(mockLogger.messages.any { it.contains("PostHog SDK is disabled because the project token is required") }) } @Test diff --git a/posthog/src/test/java/com/posthog/PostHogTest.kt b/posthog/src/test/java/com/posthog/PostHogTest.kt index 90f52559e..baea4653d 100644 --- a/posthog/src/test/java/com/posthog/PostHogTest.kt +++ b/posthog/src/test/java/com/posthog/PostHogTest.kt @@ -272,7 +272,7 @@ internal class PostHogTest { ) assertTrue(sut.isOptOut()) - assertTrue(logger.messages.any { it.contains("PostHog SDK is disabled because the API key is required") }) + assertTrue(logger.messages.any { it.contains("PostHog SDK is disabled because the project token is required") }) sut.close() } diff --git a/sdk_compliance_adapter/src/main/kotlin/com/posthog/compliance/ComplianceAdapter.kt b/sdk_compliance_adapter/src/main/kotlin/com/posthog/compliance/ComplianceAdapter.kt index fed17e506..faa5c9962 100644 --- a/sdk_compliance_adapter/src/main/kotlin/com/posthog/compliance/ComplianceAdapter.kt +++ b/sdk_compliance_adapter/src/main/kotlin/com/posthog/compliance/ComplianceAdapter.kt @@ -240,7 +240,7 @@ fun main() { call.respond( HealthResponse( sdk_name = "posthog-android", - sdk_version = PostHogConfig(apiKey = "").sdkVersion, + sdk_version = PostHogConfig(projectToken = "").sdkVersion, adapter_version = "1.0.0", // Opt into the capture suites (android posts /batch with gzip). capabilities = listOf("capture_v0", "encoding_gzip"), @@ -274,7 +274,7 @@ fun main() { val config = PostHogConfig( - apiKey = req.api_key, + projectToken = req.api_key, host = req.host, flushAt = req.flush_at ?: 1, flushIntervalSeconds = flushIntervalSeconds,