diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e8b3d3..e970f3b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- fix: beforeBreadcrumb discarding breadcrumbs on Apple platforms ([#101](https://github.com/getsentry/sentry-kotlin-multiplatform/pull/101)) + ## 0.2.0 ### Features diff --git a/sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt b/sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt index 973de837..5d73d6cd 100644 --- a/sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt +++ b/sentry-kotlin-multiplatform/src/commonAppleMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt @@ -67,8 +67,12 @@ internal fun CocoaSentryOptions.applyCocoaBaseOptions(options: SentryOptions) { PrivateSentrySDKOnly.setSdkName(sdkName, sdkVersion) beforeBreadcrumb = { cocoaBreadcrumb -> - cocoaBreadcrumb?.toKmpBreadcrumb() - ?.let { options.beforeBreadcrumb?.invoke(it) }?.toCocoaBreadcrumb() + if (options.beforeBreadcrumb == null) { + cocoaBreadcrumb + } else { + cocoaBreadcrumb?.toKmpBreadcrumb() + ?.let { options.beforeBreadcrumb?.invoke(it) }?.toCocoaBreadcrumb() + } } enableCaptureFailedRequests = options.enableCaptureFailedRequests diff --git a/sentry-kotlin-multiplatform/src/commonAppleTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt b/sentry-kotlin-multiplatform/src/commonAppleTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt new file mode 100644 index 00000000..944d7514 --- /dev/null +++ b/sentry-kotlin-multiplatform/src/commonAppleTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt @@ -0,0 +1,23 @@ +package io.sentry.kotlin.multiplatform + +import io.sentry.kotlin.multiplatform.extensions.applyCocoaBaseOptions +import io.sentry.kotlin.multiplatform.extensions.toKmpBreadcrumb +import io.sentry.kotlin.multiplatform.protocol.Breadcrumb + +actual class BreadcrumbConfigurator { + private val cocoaBreadcrumb = CocoaBreadcrumb() + actual val originalBreadcrumb: Breadcrumb = cocoaBreadcrumb.toKmpBreadcrumb() + + actual fun applyOptions(optionsConfiguration: OptionsConfiguration): Breadcrumb? { + val kmpOptions = SentryOptions() + optionsConfiguration.invoke(kmpOptions) + return applyOptions(kmpOptions) + } + + actual fun applyOptions(options: SentryOptions): Breadcrumb? { + val cocoaOptions = CocoaSentryOptions() + cocoaOptions.applyCocoaBaseOptions(options) + val cocoaModifiedBreadcrumb = cocoaOptions.beforeBreadcrumb?.invoke(cocoaBreadcrumb) + return cocoaModifiedBreadcrumb?.toKmpBreadcrumb() + } +} diff --git a/sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt b/sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt index f793dda9..8b6ef6ed 100644 --- a/sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt +++ b/sentry-kotlin-multiplatform/src/commonJvmMain/kotlin/io/sentry/kotlin/multiplatform/extensions/SentryOptionsExtensions.kt @@ -41,7 +41,11 @@ internal fun JvmSentryOptions.applyJvmBaseOptions(options: SentryOptions) { maxAttachmentSize = options.maxAttachmentSize maxBreadcrumbs = options.maxBreadcrumbs setBeforeBreadcrumb { jvmBreadcrumb, _ -> - options.beforeBreadcrumb?.invoke(jvmBreadcrumb.toKmpBreadcrumb())?.toJvmBreadcrumb() + if (options.beforeBreadcrumb == null) { + jvmBreadcrumb + } else { + options.beforeBreadcrumb?.invoke(jvmBreadcrumb.toKmpBreadcrumb())?.toJvmBreadcrumb() + } } setBeforeSend { jvmSentryEvent, hint -> if (options.beforeSend == null) { diff --git a/sentry-kotlin-multiplatform/src/commonJvmTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt b/sentry-kotlin-multiplatform/src/commonJvmTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt new file mode 100644 index 00000000..1d4912b5 --- /dev/null +++ b/sentry-kotlin-multiplatform/src/commonJvmTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt @@ -0,0 +1,25 @@ +package io.sentry.kotlin.multiplatform + +import io.sentry.Hint +import io.sentry.kotlin.multiplatform.extensions.applyJvmBaseOptions +import io.sentry.kotlin.multiplatform.extensions.toKmpBreadcrumb +import io.sentry.kotlin.multiplatform.protocol.Breadcrumb + +actual class BreadcrumbConfigurator { + private val jvmBreadcrumb = JvmBreadcrumb() + actual val originalBreadcrumb: Breadcrumb = jvmBreadcrumb.toKmpBreadcrumb() + + actual fun applyOptions(optionsConfiguration: OptionsConfiguration): Breadcrumb? { + val kmpOptions = SentryOptions() + optionsConfiguration.invoke(kmpOptions) + return applyOptions(kmpOptions) + } + + actual fun applyOptions(options: SentryOptions): Breadcrumb? { + val jvmOptions = JvmSentryOptions() + jvmOptions.applyJvmBaseOptions(options) + val jvmHint = Hint() + val jvmModifiedBreadcrumb = jvmOptions.beforeBreadcrumb?.execute(jvmBreadcrumb, jvmHint) + return jvmModifiedBreadcrumb?.toKmpBreadcrumb() + } +} diff --git a/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeBreadcrumbIntegrationTest.kt b/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeBreadcrumbIntegrationTest.kt new file mode 100644 index 00000000..27fe6165 --- /dev/null +++ b/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeBreadcrumbIntegrationTest.kt @@ -0,0 +1,137 @@ +package io.sentry.kotlin.multiplatform + +import io.sentry.kotlin.multiplatform.utils.fakeDsn +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +class BeforeBreadcrumbIntegrationTest { + private val breadcrumbConfigurator = BreadcrumbConfigurator() + + @Test + fun `breadcrumb is not null if KMP beforeBreadcrumb callback config is null`() { + val breadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + } + assertNotNull(breadcrumb) + } + + @Test + fun `breadcrumb is null if KMP beforeBreadcrumb callback config returns null`() { + val breadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { + null + } + } + assertNull(breadcrumb) + } + + @Test + fun `breadcrumb is not null if KMP beforeBreadcrumb callback config returns not null`() { + val breadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb + } + } + assertNotNull(breadcrumb) + } + + @Test + fun `breadcrumb level is not modified if KMP beforeBreadcrumb callback config does not modify it`() { + val originalBreadcrumb = breadcrumbConfigurator.originalBreadcrumb + val modifiedBreadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb + } + } + assertEquals(originalBreadcrumb.level, modifiedBreadcrumb?.level) + } + + @Test + fun `breadcrumb level is modified if KMP beforeBreadcrumb callback config modifies it`() { + val modifiedBreadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb.level = SentryLevel.WARNING + breadcrumb + } + } + assertEquals(SentryLevel.WARNING, modifiedBreadcrumb?.level) + } + + @Test + fun `breadcrumb category is not modified if KMP beforeBreadcrumb callback config does not modify it`() { + val originalBreadcrumb = breadcrumbConfigurator.originalBreadcrumb + val modifiedBreadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb + } + } + assertEquals(originalBreadcrumb.category, modifiedBreadcrumb?.category) + } + + @Test + fun `breadcrumb category is modified if KMP beforeBreadcrumb callback config modifies it`() { + val modifiedBreadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb.category = "category" + breadcrumb + } + } + assertEquals("category", modifiedBreadcrumb?.category) + } + + @Test + fun `breadcrumb type is not modified if KMP beforeBreadcrumb callback config does not modify it`() { + val originalBreadcrumb = breadcrumbConfigurator.originalBreadcrumb + val modifiedBreadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb + } + } + assertEquals(originalBreadcrumb.type, modifiedBreadcrumb?.type) + } + + @Test + fun `breadcrumb type is modified if KMP beforeBreadcrumb callback config modifies it`() { + val modifiedBreadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb.type = "type" + breadcrumb + } + } + assertEquals("type", modifiedBreadcrumb?.type) + } + + @Test + fun `breadcrumb message is not modified if KMP beforeBreadcrumb callback config does not modify it`() { + val originalBreadcrumb = breadcrumbConfigurator.originalBreadcrumb + val modifiedBreadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb + } + } + assertEquals(originalBreadcrumb.level, modifiedBreadcrumb?.level) + } + + @Test + fun `breadcrumb message is modified if KMP beforeBreadcrumb callback config modifies it`() { + val modifiedBreadcrumb = breadcrumbConfigurator.applyOptions { + it.dsn = fakeDsn + it.beforeBreadcrumb = { breadcrumb -> + breadcrumb.message = "message" + breadcrumb + } + } + assertEquals("message", modifiedBreadcrumb?.message) + } +} diff --git a/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeBreadcrumbTest.kt b/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeBreadcrumbTest.kt new file mode 100644 index 00000000..aad7f79a --- /dev/null +++ b/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BeforeBreadcrumbTest.kt @@ -0,0 +1,19 @@ +package io.sentry.kotlin.multiplatform + +import io.sentry.kotlin.multiplatform.protocol.Breadcrumb +import kotlin.test.Test +import kotlin.test.assertEquals + +class BeforeBreadcrumbTest { + @Test + fun `beforeBreadcrumb drops breadcrumb`() { + val options = SentryOptions() + options.beforeBreadcrumb = { + null + } + + val breadcrumb = options.beforeBreadcrumb?.invoke(Breadcrumb()) + + assertEquals(null, breadcrumb) + } +} diff --git a/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt b/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt new file mode 100644 index 00000000..4dcda258 --- /dev/null +++ b/sentry-kotlin-multiplatform/src/commonTest/kotlin/io/sentry/kotlin/multiplatform/BreadcrumbConfigurator.kt @@ -0,0 +1,13 @@ +package io.sentry.kotlin.multiplatform + +import io.sentry.kotlin.multiplatform.protocol.Breadcrumb + +/** + * This class deals with configuring and modifying a Breadcrumb. + * It is used to test any code that can alter a Breadcrumb. + */ +expect class BreadcrumbConfigurator() { + val originalBreadcrumb: Breadcrumb + fun applyOptions(optionsConfiguration: OptionsConfiguration): Breadcrumb? + fun applyOptions(options: SentryOptions = SentryOptions()): Breadcrumb? +}