From 89da1a3eac97d21f4c4a15c22b6a125fbf1edcc9 Mon Sep 17 00:00:00 2001 From: Manoel Aranda Neto <5731772+marandaneto@users.noreply.github.com> Date: Tue, 21 Jul 2020 15:46:02 +0200 Subject: [PATCH] fix: envelope header has sdkVersion and not sdkInfo (#488) --- .../core/AndroidOptionsInitializer.java | 3 - .../core/DefaultAndroidEventProcessor.java | 19 +-- .../android/core/SentryAndroidOptions.java | 21 +++ .../core/AndroidOptionsInitializerTest.kt | 22 +-- .../android/core/AndroidSerializerTest.kt | 55 ++++--- .../core/DefaultAndroidEventProcessorTest.kt | 17 +-- ...fo.txt => envelope_session_sdkversion.txt} | 2 +- sentry-android-ndk/build.gradle.kts | 9 +- .../java/io/sentry/android/ndk/SentryNdk.java | 6 +- .../io/sentry/android/ndk/SentryNdkUtil.java | 24 ++++ .../sentry/android/ndk/SentryNdkUtilTest.kt | 30 ++++ sentry-android-timber/build.gradle.kts | 5 - .../android/timber/SentryTimberIntegration.kt | 6 + .../timber/SentryTimberIntegrationTest.kt | 16 ++- .../java/io/sentry/core/EnvelopeSender.java | 3 +- .../java/io/sentry/core/SentryClient.java | 3 +- .../java/io/sentry/core/SentryEnvelope.java | 15 +- .../io/sentry/core/SentryEnvelopeHeader.java | 13 +- .../core/SentryEnvelopeHeaderAdapter.java | 134 +++++++++++++----- .../java/io/sentry/core/SentryOptions.java | 22 +-- .../io/sentry/core/cache/SessionCache.java | 4 +- .../java/io/sentry/core/protocol/SdkInfo.java | 35 ----- .../io/sentry/core/protocol/SdkVersion.java | 9 ++ .../io/sentry/core/util/IntegerUtils.java | 28 ---- .../java/io/sentry/core/SentryClientTest.kt | 9 +- .../io/sentry/core/protocol/SdkInfoTest.kt | 53 ------- .../io/sentry/core/util/IntegerUtilsTest.kt | 28 ---- 27 files changed, 309 insertions(+), 282 deletions(-) rename sentry-android-core/src/test/resources/{envelope_session_sdkinfo.txt => envelope_session_sdkversion.txt} (72%) create mode 100644 sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdkUtil.java create mode 100644 sentry-android-ndk/src/test/java/io/sentry/android/ndk/SentryNdkUtilTest.kt delete mode 100644 sentry-core/src/main/java/io/sentry/core/util/IntegerUtils.java delete mode 100644 sentry-core/src/test/java/io/sentry/core/protocol/SdkInfoTest.kt delete mode 100644 sentry-core/src/test/java/io/sentry/core/util/IntegerUtilsTest.kt diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java index 73ceece7e..aac90401e 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AndroidOptionsInitializer.java @@ -13,7 +13,6 @@ import io.sentry.core.SendFireAndForgetEnvelopeSender; import io.sentry.core.SentryLevel; import io.sentry.core.SentryOptions; -import io.sentry.core.protocol.SdkInfo; import io.sentry.core.util.Objects; import java.io.File; import org.jetbrains.annotations.NotNull; @@ -100,8 +99,6 @@ static void init( options.setLogger(logger); options.setSentryClientName(BuildConfig.SENTRY_CLIENT_NAME + "/" + BuildConfig.VERSION_NAME); - options.setSdkInfo( - SdkInfo.createSdkInfo(BuildConfig.SENTRY_CLIENT_NAME, BuildConfig.VERSION_NAME)); ManifestMetadataReader.applyMetadata(context, options); initializeCacheDirs(context, options); diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java index b0bcf15b0..92cb11e35 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/DefaultAndroidEventProcessor.java @@ -33,7 +33,6 @@ import io.sentry.core.protocol.DebugMeta; import io.sentry.core.protocol.Device; import io.sentry.core.protocol.OperatingSystem; -import io.sentry.core.protocol.SdkVersion; import io.sentry.core.protocol.SentryThread; import io.sentry.core.protocol.User; import io.sentry.core.util.ApplyScopeUtils; @@ -176,7 +175,7 @@ private void processNonCachedEvent(final @NotNull SentryEvent event) { event.setDebugMeta(getDebugMeta()); } if (event.getSdk() == null) { - event.setSdk(getSdkVersion()); + event.setSdk(options.getSdkVersion()); } PackageInfo packageInfo = ContextUtils.getPackageInfo(context, logger); @@ -235,7 +234,6 @@ private void processNonCachedEvent(final @NotNull SentryEvent event) { DebugMeta debugMeta = new DebugMeta(); debugMeta.setImages(debugImages); - debugMeta.setSdkInfo(options.getSdkInfo()); return debugMeta; } @@ -244,21 +242,6 @@ private void setAppExtras(final @NotNull App app) { app.setAppStartTime(appStartTime); } - private @NotNull SdkVersion getSdkVersion() { - SdkVersion sdkVersion = new SdkVersion(); - - sdkVersion.setName("sentry.java.android"); - String version = BuildConfig.VERSION_NAME; - sdkVersion.setVersion(version); - sdkVersion.addPackage("maven:sentry-core", version); - sdkVersion.addPackage("maven:sentry-android-core", version); - if (options.isEnableNdk()) { - sdkVersion.addPackage("maven:sentry-android-ndk", version); - } - - return sdkVersion; - } - @SuppressWarnings("deprecation") private @NotNull String getAbi() { return Build.CPU_ABI; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java index 34c16d201..3eb14cda1 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/SentryAndroidOptions.java @@ -1,7 +1,9 @@ package io.sentry.android.core; import io.sentry.core.SentryOptions; +import io.sentry.core.protocol.SdkVersion; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; /** Sentry SDK options for Android */ public final class SentryAndroidOptions extends SentryOptions { @@ -32,6 +34,25 @@ public final class SentryAndroidOptions extends SentryOptions { /** Enable or disable automatic breadcrumbs for App Components Using ComponentCallbacks */ private boolean enableAppComponentBreadcrumbs = true; + public SentryAndroidOptions() { + setSdkVersion(createSdkVersion()); + } + + private @NotNull SdkVersion createSdkVersion() { + final SdkVersion sdkVersion = new SdkVersion(); + + sdkVersion.setName(BuildConfig.SENTRY_CLIENT_NAME); + String version = BuildConfig.VERSION_NAME; + sdkVersion.setVersion(version); + + // add 2 default packages + sdkVersion.addPackage("maven:sentry-android-core", version); + // TODO: sentry-core should add itself as the version may mismatch + sdkVersion.addPackage("maven:sentry-core", version); + + return sdkVersion; + } + /** * Checks if ANR (Application Not Responding) is enabled or disabled Default is enabled * diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt index 431518a48..6d8026e20 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidOptionsInitializerTest.kt @@ -323,22 +323,28 @@ class AndroidOptionsInitializerTest { } @Test - fun `init should set SdkInfo`() { + fun `init should set SdkVersion`() { val sentryOptions = SentryAndroidOptions() val mockContext = mock() whenever(mockContext.applicationContext).thenReturn(null) AndroidOptionsInitializer.init(sentryOptions, mockContext) - assertNotNull(sentryOptions.sdkInfo) - val sdkInfo = sentryOptions.sdkInfo!! + assertNotNull(sentryOptions.sdkVersion) + val sdkVersion = sentryOptions.sdkVersion!! - assertEquals("sentry.java.android", sdkInfo.sdkName) + assertEquals(BuildConfig.SENTRY_CLIENT_NAME, sdkVersion.name) + assertEquals(BuildConfig.VERSION_NAME, sdkVersion.version) - // versions change on every release, so lets check only if its not null - assertNotNull(sdkInfo.versionMajor) - assertNotNull(sdkInfo.versionMinor) - assertNotNull(sdkInfo.versionPatchlevel) + assertTrue(sdkVersion.packages!!.any { + it.name == "maven:sentry-android-core" + it.version == BuildConfig.VERSION_NAME + }) + + assertTrue(sdkVersion.packages!!.any { + it.name == "maven:sentry-core" + it.version == BuildConfig.VERSION_NAME + }) } private fun createMockContext(): Context { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidSerializerTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidSerializerTest.kt index 5437edaee..660b3381e 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AndroidSerializerTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AndroidSerializerTest.kt @@ -14,7 +14,7 @@ import io.sentry.core.SentryLevel import io.sentry.core.Session import io.sentry.core.protocol.Contexts import io.sentry.core.protocol.Device -import io.sentry.core.protocol.SdkInfo +import io.sentry.core.protocol.SdkVersion import java.io.ByteArrayInputStream import java.io.IOException import java.io.InputStream @@ -358,16 +358,24 @@ class AndroidSerializerTest { } @Test - fun `When deserializing an Envelope, sdkInfo should be set`() { - val jsonEnvelope = FileFromResources.invoke("envelope_session_sdkinfo.txt") + fun `When deserializing an Envelope, SdkVersion should be set`() { + val jsonEnvelope = FileFromResources.invoke("envelope_session_sdkversion.txt") val envelope = serializer.deserializeEnvelope(ByteArrayInputStream(jsonEnvelope.toByteArray(Charsets.UTF_8)))!! - assertNotNull(envelope.header.sdkInfo) - val sdkInfo = envelope.header.sdkInfo!! + assertNotNull(envelope.header.sdkVersion) + val sdkInfo = envelope.header.sdkVersion!! - assertEquals("test", sdkInfo.sdkName) - assertEquals(1, sdkInfo.versionMajor) - assertEquals(2, sdkInfo.versionMinor) - assertEquals(3, sdkInfo.versionPatchlevel) + assertEquals("test", sdkInfo.name) + assertEquals("1.2.3", sdkInfo.version) + + assertNotNull(sdkInfo.integrations) + assertTrue(sdkInfo.integrations!!.any { it == "NdkIntegration" }) + + assertNotNull(sdkInfo.packages) + + assertTrue(sdkInfo.packages!!.any { + it.name == "maven:sentry-android-core" + it.version == "4.5.6" + }) } @Test @@ -382,20 +390,33 @@ class AndroidSerializerTest { } @Test - fun `When serializing an envelope, sdkInfo should be set`() { + fun `When serializing an envelope, SdkVersion should be set`() { val session = createSessionMockData() - val sentryEnvelope = SentryEnvelope.fromSession(serializer, session, SdkInfo.createSdkInfo("test", "1.2.3")) + val version = SdkVersion().apply { + name = "test" + version = "1.2.3" + addIntegration("TestIntegration") + addPackage("abc", "4.5.6") + } + val sentryEnvelope = SentryEnvelope.fromSession(serializer, session, version) val jsonEnvelope = serializeToString(sentryEnvelope) // reversing it so we can assert the values val envelope = serializer.deserializeEnvelope(ByteArrayInputStream(jsonEnvelope.toByteArray(Charsets.UTF_8)))!! - assertNotNull(envelope.header.sdkInfo) + assertNotNull(envelope.header.sdkVersion) + + val sdkVersion = envelope.header.sdkVersion!! + assertEquals(version.name, sdkVersion.name) + assertEquals(version.version, sdkVersion.version) + + assertNotNull(sdkVersion.integrations) + assertTrue(sdkVersion.integrations!!.any { it == "TestIntegration" }) - val sdkInfo = envelope.header.sdkInfo!! - assertEquals("test", sdkInfo.sdkName) - assertEquals(1, sdkInfo.versionMajor) - assertEquals(2, sdkInfo.versionMinor) - assertEquals(3, sdkInfo.versionPatchlevel) + assertNotNull(sdkVersion.packages) + assertTrue(sdkVersion.packages!!.any { + it.name == "abc" + it.version == "4.5.6" + }) } private fun assertSessionData(expectedSession: Session?) { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt index 6e269c6a9..13f9082b2 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/DefaultAndroidEventProcessorTest.kt @@ -18,7 +18,7 @@ import io.sentry.core.DiagnosticLogger import io.sentry.core.SentryEvent import io.sentry.core.SentryLevel import io.sentry.core.SentryOptions -import io.sentry.core.protocol.SdkInfo +import io.sentry.core.protocol.SdkVersion import io.sentry.core.protocol.SentryThread import kotlin.test.BeforeTest import kotlin.test.Test @@ -27,6 +27,7 @@ import kotlin.test.assertFailsWith import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull +import kotlin.test.assertSame import kotlin.test.assertTrue import org.junit.runner.RunWith @@ -39,7 +40,10 @@ class DefaultAndroidEventProcessorTest { val options = SentryOptions().apply { isDebug = true setLogger(mock()) - sdkInfo = SdkInfo.createSdkInfo("test", "1.2.3") + sdkVersion = SdkVersion().apply { + name = "test" + version = "1.2.3" + } } } @@ -165,14 +169,11 @@ class DefaultAndroidEventProcessorTest { } @Test - fun `Processor sets sdkInfo in the event`() { + fun `Processor sets sdkVersion in the event`() { val processor = DefaultAndroidEventProcessor(context, fixture.options, fixture.buildInfo, mock()) val event = SentryEvent() processor.process(event, null) - assertNotNull(event.debugMeta.sdkInfo) - assertEquals("test", event.debugMeta.sdkInfo.sdkName) - assertEquals(1, event.debugMeta.sdkInfo.versionMajor) - assertEquals(2, event.debugMeta.sdkInfo.versionMinor) - assertEquals(3, event.debugMeta.sdkInfo.versionPatchlevel) + assertNotNull(event.sdk) + assertSame(event.sdk, fixture.options.sdkVersion) } } diff --git a/sentry-android-core/src/test/resources/envelope_session_sdkinfo.txt b/sentry-android-core/src/test/resources/envelope_session_sdkversion.txt similarity index 72% rename from sentry-android-core/src/test/resources/envelope_session_sdkinfo.txt rename to sentry-android-core/src/test/resources/envelope_session_sdkversion.txt index 26c3fc32f..b2c96d1ca 100644 --- a/sentry-android-core/src/test/resources/envelope_session_sdkinfo.txt +++ b/sentry-android-core/src/test/resources/envelope_session_sdkversion.txt @@ -1,3 +1,3 @@ -{"sdk_info":{"sdk_name":"test","version_major":1,"version_minor":2,"version_patchlevel":3}} +{"sdk":{"name":"test","version":"1.2.3","integrations":["NdkIntegration"],"packages":[{"name":"maven:sentry-android-core","version":"4.5.6"}]}} {"content_type":"application/json","type":"session","length":306} {"sid":"c81d4e2e-bcf2-11e6-869b-7df92533d2db","did":"123","init":true,"started":"2020-02-07T14:16:00Z","status":"ok","seq":123456,"errors":2,"duration":6000.0,"timestamp":"2020-02-07T14:16:00Z","attrs":{"release":"io.sentry@1.0+123","environment":"debug","ip_address":"127.0.0.1","user_agent":"jamesBond"}} diff --git a/sentry-android-ndk/build.gradle.kts b/sentry-android-ndk/build.gradle.kts index 4a27ba111..dee6a9235 100644 --- a/sentry-android-ndk/build.gradle.kts +++ b/sentry-android-ndk/build.gradle.kts @@ -1,4 +1,5 @@ import com.novoda.gradle.release.PublishExtension +import org.jetbrains.kotlin.config.KotlinCompilerVersion plugins { id("com.android.library") @@ -42,11 +43,6 @@ android { } } - buildFeatures { - // Determines whether to generate a BuildConfig class. - buildConfig = false - } - externalNativeBuild { cmake { version = Config.Android.cmakeVersion @@ -103,6 +99,9 @@ dependencies { api(project(":sentry-android-core")) compileOnly(Config.CompileOnly.jetbrainsAnnotations) + + testImplementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION)) + testImplementation(Config.TestLibs.kotlinTestJunit) } val initNative = tasks.register("initNative") { diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java index 4bc028802..aaecb89f8 100644 --- a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdk.java @@ -2,6 +2,7 @@ import io.sentry.core.SentryOptions; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; @ApiStatus.Internal public final class SentryNdk { @@ -19,9 +20,10 @@ private SentryNdk() {} System.loadLibrary("sentry-android"); } - private static native void initSentryNative(SentryOptions options); + private static native void initSentryNative(@NotNull final SentryOptions options); - public static void init(SentryOptions options) { + public static void init(@NotNull final SentryOptions options) { + SentryNdkUtil.addPackage(options.getSdkVersion()); initSentryNative(options); } } diff --git a/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdkUtil.java b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdkUtil.java new file mode 100644 index 000000000..1b492aa04 --- /dev/null +++ b/sentry-android-ndk/src/main/java/io/sentry/android/ndk/SentryNdkUtil.java @@ -0,0 +1,24 @@ +package io.sentry.android.ndk; + +import io.sentry.core.protocol.SdkVersion; +import org.jetbrains.annotations.Nullable; + +/** + * Util class to make SentryNdk testable, as SentryNdk inits native libraries and it breaks on init. + */ +final class SentryNdkUtil { + + private SentryNdkUtil() {} + + /** + * Adds the sentry-android-ndk package into the package list + * + * @param sdkVersion the SdkVersion object + */ + static void addPackage(@Nullable final SdkVersion sdkVersion) { + if (sdkVersion == null) { + return; + } + sdkVersion.addPackage("maven:sentry-android-ndk", BuildConfig.VERSION_NAME); + } +} diff --git a/sentry-android-ndk/src/test/java/io/sentry/android/ndk/SentryNdkUtilTest.kt b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/SentryNdkUtilTest.kt new file mode 100644 index 000000000..f64d6b097 --- /dev/null +++ b/sentry-android-ndk/src/test/java/io/sentry/android/ndk/SentryNdkUtilTest.kt @@ -0,0 +1,30 @@ +package io.sentry.android.ndk + +import io.sentry.core.SentryOptions +import io.sentry.core.protocol.SdkVersion +import kotlin.test.Test +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class SentryNdkUtilTest { + + @Test + fun `SentryNdk adds the Ndk package into the package list`() { + val options = SentryOptions().apply { + sdkVersion = SdkVersion() + } + SentryNdkUtil.addPackage(options.sdkVersion) + assertTrue(options.sdkVersion!!.packages!!.any { + it.name == "maven:sentry-android-ndk" + it.version == BuildConfig.VERSION_NAME + }) + } + + @Test + fun `SentryNdk do not add the Ndk package into the package list`() { + val options = SentryOptions() + SentryNdkUtil.addPackage(options.sdkVersion) + + assertNull(options.sdkVersion?.packages) + } +} diff --git a/sentry-android-timber/build.gradle.kts b/sentry-android-timber/build.gradle.kts index ca751c0b5..75a857800 100644 --- a/sentry-android-timber/build.gradle.kts +++ b/sentry-android-timber/build.gradle.kts @@ -33,11 +33,6 @@ android { } } - buildFeatures { - // Determines whether to generate a BuildConfig class. - buildConfig = false - } - compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 diff --git a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt index 4d4e16505..0ddb31e85 100644 --- a/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt +++ b/sentry-android-timber/src/main/java/io/sentry/android/timber/SentryTimberIntegration.kt @@ -5,6 +5,7 @@ import io.sentry.core.ILogger import io.sentry.core.Integration import io.sentry.core.SentryLevel import io.sentry.core.SentryOptions +import io.sentry.core.protocol.SdkVersion import java.io.Closeable import timber.log.Timber @@ -19,6 +20,7 @@ class SentryTimberIntegration( private lateinit var logger: ILogger override fun register(hub: IHub, options: SentryOptions) { + addPackage(options.sdkVersion) logger = options.logger tree = SentryTimberTree(hub, minEventLevel, minBreadcrumbLevel) @@ -36,4 +38,8 @@ class SentryTimberIntegration( } } } + + private fun addPackage(sdkVersion: SdkVersion?) { + sdkVersion?.addPackage("maven:sentry-android-timber", BuildConfig.VERSION_NAME) + } } diff --git a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt index ab6eab60a..3eb307216 100644 --- a/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt +++ b/sentry-android-timber/src/test/java/io/sentry/android/timber/SentryTimberIntegrationTest.kt @@ -6,6 +6,7 @@ import com.nhaarman.mockitokotlin2.verify import io.sentry.core.IHub import io.sentry.core.SentryLevel import io.sentry.core.SentryOptions +import io.sentry.core.protocol.SdkVersion import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals @@ -16,7 +17,9 @@ class SentryTimberIntegrationTest { private class Fixture { val hub = mock() - val options = SentryOptions() + val options = SentryOptions().apply { + sdkVersion = SdkVersion() + } fun getSut( minEventLevel: SentryLevel = SentryLevel.ERROR, @@ -82,4 +85,15 @@ class SentryTimberIntegrationTest { assertEquals(sut.minEventLevel, SentryLevel.INFO) assertEquals(sut.minBreadcrumbLevel, SentryLevel.DEBUG) } + + @Test + fun `Integrations adds itself to the package list`() { + val sut = fixture.getSut() + sut.register(fixture.hub, fixture.options) + + assertTrue(fixture.options.sdkVersion!!.packages!!.any { + it.name == "maven:sentry-android-timber" + it.version == BuildConfig.VERSION_NAME + }) + } } diff --git a/sentry-core/src/main/java/io/sentry/core/EnvelopeSender.java b/sentry-core/src/main/java/io/sentry/core/EnvelopeSender.java index e008aac28..17c228ba5 100644 --- a/sentry-core/src/main/java/io/sentry/core/EnvelopeSender.java +++ b/sentry-core/src/main/java/io/sentry/core/EnvelopeSender.java @@ -173,7 +173,8 @@ private void processEnvelope(final @NotNull SentryEnvelope envelope, final @Null } else { // TODO: Bundle all session in a single envelope hub.captureEnvelope( - SentryEnvelope.fromSession(serializer, session, envelope.getHeader().getSdkInfo()), + SentryEnvelope.fromSession( + serializer, session, envelope.getHeader().getSdkVersion()), hint); logger.log(SentryLevel.DEBUG, "Item %d is being captured.", items); diff --git a/sentry-core/src/main/java/io/sentry/core/SentryClient.java b/sentry-core/src/main/java/io/sentry/core/SentryClient.java index 7e57b39e9..2bc016f4b 100644 --- a/sentry-core/src/main/java/io/sentry/core/SentryClient.java +++ b/sentry-core/src/main/java/io/sentry/core/SentryClient.java @@ -210,7 +210,8 @@ public void captureSession(final @NotNull Session session, final @Nullable Objec SentryEnvelope envelope; try { - envelope = SentryEnvelope.fromSession(options.getSerializer(), session, options.getSdkInfo()); + envelope = + SentryEnvelope.fromSession(options.getSerializer(), session, options.getSdkVersion()); } catch (IOException e) { options.getLogger().log(SentryLevel.ERROR, "Failed to capture session.", e); return; diff --git a/sentry-core/src/main/java/io/sentry/core/SentryEnvelope.java b/sentry-core/src/main/java/io/sentry/core/SentryEnvelope.java index ee4afb760..006c83a56 100644 --- a/sentry-core/src/main/java/io/sentry/core/SentryEnvelope.java +++ b/sentry-core/src/main/java/io/sentry/core/SentryEnvelope.java @@ -1,6 +1,6 @@ package io.sentry.core; -import io.sentry.core.protocol.SdkInfo; +import io.sentry.core.protocol.SdkVersion; import io.sentry.core.protocol.SentryId; import io.sentry.core.util.Objects; import java.io.IOException; @@ -35,19 +35,19 @@ public SentryEnvelope( public SentryEnvelope( final @Nullable SentryId eventId, - final @Nullable SdkInfo sdkInfo, + final @Nullable SdkVersion sdkVersion, final @NotNull Iterable items) { - header = new SentryEnvelopeHeader(eventId, sdkInfo); + header = new SentryEnvelopeHeader(eventId, sdkVersion); this.items = Objects.requireNonNull(items, "SentryEnvelope items are required."); } public SentryEnvelope( final @Nullable SentryId eventId, - final @Nullable SdkInfo sdkInfo, + final @Nullable SdkVersion sdkVersion, final @NotNull SentryEnvelopeItem item) { Objects.requireNonNull(item, "SentryEnvelopeItem is required."); - header = new SentryEnvelopeHeader(eventId, sdkInfo); + header = new SentryEnvelopeHeader(eventId, sdkVersion); final List items = new ArrayList<>(1); items.add(item); this.items = items; @@ -56,11 +56,12 @@ public SentryEnvelope( public static @NotNull SentryEnvelope fromSession( final @NotNull ISerializer serializer, final @NotNull Session session, - final @Nullable SdkInfo sdkInfo) + final @Nullable SdkVersion sdkVersion) throws IOException { Objects.requireNonNull(serializer, "Serializer is required."); Objects.requireNonNull(session, "session is required."); - return new SentryEnvelope(null, sdkInfo, SentryEnvelopeItem.fromSession(serializer, session)); + return new SentryEnvelope( + null, sdkVersion, SentryEnvelopeItem.fromSession(serializer, session)); } } diff --git a/sentry-core/src/main/java/io/sentry/core/SentryEnvelopeHeader.java b/sentry-core/src/main/java/io/sentry/core/SentryEnvelopeHeader.java index a917932a5..03a6e14a1 100644 --- a/sentry-core/src/main/java/io/sentry/core/SentryEnvelopeHeader.java +++ b/sentry-core/src/main/java/io/sentry/core/SentryEnvelopeHeader.java @@ -1,6 +1,6 @@ package io.sentry.core; -import io.sentry.core.protocol.SdkInfo; +import io.sentry.core.protocol.SdkVersion; import io.sentry.core.protocol.SentryId; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -11,11 +11,12 @@ public final class SentryEnvelopeHeader { // (e.g: attachments, user feedback) private final @Nullable SentryId eventId; - private final @Nullable SdkInfo sdkInfo; + private final @Nullable SdkVersion sdkVersion; - public SentryEnvelopeHeader(final @Nullable SentryId eventId, final @Nullable SdkInfo sdkInfo) { + public SentryEnvelopeHeader( + final @Nullable SentryId eventId, final @Nullable SdkVersion sdkVersion) { this.eventId = eventId; - this.sdkInfo = sdkInfo; + this.sdkVersion = sdkVersion; } public SentryEnvelopeHeader(final @Nullable SentryId eventId) { @@ -30,7 +31,7 @@ public SentryEnvelopeHeader() { return eventId; } - public @Nullable SdkInfo getSdkInfo() { - return sdkInfo; + public @Nullable SdkVersion getSdkVersion() { + return sdkVersion; } } diff --git a/sentry-core/src/main/java/io/sentry/core/SentryEnvelopeHeaderAdapter.java b/sentry-core/src/main/java/io/sentry/core/SentryEnvelopeHeaderAdapter.java index 82e0088f5..386098bb7 100644 --- a/sentry-core/src/main/java/io/sentry/core/SentryEnvelopeHeaderAdapter.java +++ b/sentry-core/src/main/java/io/sentry/core/SentryEnvelopeHeaderAdapter.java @@ -4,10 +4,13 @@ import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; -import io.sentry.core.protocol.SdkInfo; +import io.sentry.core.protocol.SdkVersion; import io.sentry.core.protocol.SentryId; +import io.sentry.core.protocol.SentryPackage; import java.io.IOException; +import java.util.List; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; @ApiStatus.Internal public final class SentryEnvelopeHeaderAdapter extends TypeAdapter { @@ -25,32 +28,46 @@ public void write(JsonWriter writer, SentryEnvelopeHeader value) throws IOExcept writer.value(value.getEventId().toString()); } - final SdkInfo sdkInfo = value.getSdkInfo(); - if (sdkInfo != null) { - boolean hasInitAttrs = false; + final SdkVersion sdkVersion = value.getSdkVersion(); + if (sdkVersion != null) { + if (hasValidSdkVersion(sdkVersion)) { + writer.name("sdk").beginObject(); - if (sdkInfo.getSdkName() != null) { - hasInitAttrs = initAttrs(writer, hasInitAttrs); - writer.name("sdk_name").value(sdkInfo.getSdkName()); + writer.name("name").value(sdkVersion.getName()); + writer.name("version").value(sdkVersion.getVersion()); - // we only add versions if the name is not null - if (sdkInfo.getVersionMajor() != null) { - hasInitAttrs = initAttrs(writer, hasInitAttrs); - writer.name("version_major").value(sdkInfo.getVersionMajor()); - } - if (sdkInfo.getVersionMinor() != null) { - hasInitAttrs = initAttrs(writer, hasInitAttrs); - writer.name("version_minor").value(sdkInfo.getVersionMinor()); - } - if (sdkInfo.getVersionPatchlevel() != null) { - hasInitAttrs = initAttrs(writer, hasInitAttrs); - writer.name("version_patchlevel").value(sdkInfo.getVersionPatchlevel()); + final List integrations = sdkVersion.getIntegrations(); + if (integrations != null) { + writer.name("integrations").beginArray(); + + for (final String integration : integrations) { + writer.value(integration); + } + + // integrations + writer.endArray(); } - } - // we don't write the unknown fields for the envelope header, we skip them + final List packages = sdkVersion.getPackages(); + if (packages != null) { + writer.name("packages").beginArray(); + + for (final SentryPackage item : packages) { + // item packages + writer.beginObject(); - if (hasInitAttrs) { + writer.name("name").value(item.getName()); + writer.name("version").value(item.getVersion()); + + // item packages + writer.endObject(); + } + + // packages + writer.endArray(); + } + + // sdk writer.endObject(); } } @@ -58,11 +75,17 @@ public void write(JsonWriter writer, SentryEnvelopeHeader value) throws IOExcept writer.endObject(); } - private boolean initAttrs(JsonWriter writer, boolean hasInitAtts) throws IOException { - if (!hasInitAtts) { - writer.name("sdk_info").beginObject(); - } - return true; + /** + * Returns if SdkVersion is a valid object, with non empty name and version + * + * @param sdkVersion the SdkVersion object + * @return true if contains a valid name and version or false otherwise + */ + private boolean hasValidSdkVersion(final @NotNull SdkVersion sdkVersion) { + return sdkVersion.getName() != null + && !sdkVersion.getName().isEmpty() + && sdkVersion.getVersion() != null + && !sdkVersion.getVersion().isEmpty(); } @Override @@ -73,7 +96,7 @@ public SentryEnvelopeHeader read(JsonReader reader) throws IOException { } SentryId eventId = null; - SdkInfo sdkInfo = null; + SdkVersion sdkVersion = null; reader.beginObject(); while (reader.hasNext()) { @@ -81,24 +104,57 @@ public SentryEnvelopeHeader read(JsonReader reader) throws IOException { case "event_id": eventId = new SentryId(reader.nextString()); break; - case "sdk_info": + case "sdk": { reader.beginObject(); - sdkInfo = new SdkInfo(); + sdkVersion = new SdkVersion(); while (reader.hasNext()) { switch (reader.nextName()) { - case "sdk_name": - sdkInfo.setSdkName(reader.nextString()); + case "name": + sdkVersion.setName(reader.nextString()); break; - case "version_major": - sdkInfo.setVersionMajor(reader.nextInt()); + case "version": + sdkVersion.setVersion(reader.nextString()); break; - case "version_minor": - sdkInfo.setVersionMinor(reader.nextInt()); + case "integrations": + reader.beginArray(); + + while (reader.hasNext()) { + sdkVersion.addIntegration(reader.nextString()); + } + reader.endArray(); break; - case "version_patchlevel": - sdkInfo.setVersionPatchlevel(reader.nextInt()); + case "packages": + // packages + reader.beginArray(); + + while (reader.hasNext()) { + // packages item + reader.beginObject(); + + String name = null; + String version = null; + while (reader.hasNext()) { + switch (reader.nextName()) { + case "name": + name = reader.nextString(); + break; + case "version": + version = reader.nextString(); + break; + default: + reader.skipValue(); + } + } + sdkVersion.addPackage(name, version); + + // packages item + reader.endObject(); + } + + // packages + reader.endArray(); break; default: reader.skipValue(); @@ -118,6 +174,6 @@ public SentryEnvelopeHeader read(JsonReader reader) throws IOException { } reader.endObject(); - return new SentryEnvelopeHeader(eventId, sdkInfo); + return new SentryEnvelopeHeader(eventId, sdkVersion); } } diff --git a/sentry-core/src/main/java/io/sentry/core/SentryOptions.java b/sentry-core/src/main/java/io/sentry/core/SentryOptions.java index c796265b4..952054fc9 100644 --- a/sentry-core/src/main/java/io/sentry/core/SentryOptions.java +++ b/sentry-core/src/main/java/io/sentry/core/SentryOptions.java @@ -1,7 +1,7 @@ package io.sentry.core; import com.jakewharton.nopen.annotation.Open; -import io.sentry.core.protocol.SdkInfo; +import io.sentry.core.protocol.SdkVersion; import io.sentry.core.transport.ITransport; import io.sentry.core.transport.ITransportGate; import java.io.File; @@ -190,8 +190,8 @@ public class SentryOptions { /** whether to ignore TLS errors */ private boolean bypassSecurity = false; - /** SdkInfo object that contains the Sentry Client Name and its version */ - private @Nullable SdkInfo sdkInfo; + /** SdkVersion object that contains the Sentry Client Name and its version */ + private @Nullable SdkVersion sdkVersion; /** * Adds an event processor @@ -895,22 +895,22 @@ public void setBypassSecurity(boolean bypassSecurity) { } /** - * Returns the SdkInfo object + * Returns the SdkVersion object * - * @return the SdkInfo object or null + * @return the SdkVersion object or null */ - public @Nullable SdkInfo getSdkInfo() { - return sdkInfo; + public @Nullable SdkVersion getSdkVersion() { + return sdkVersion; } /** - * Sets the SdkInfo object + * Sets the SdkVersion object * - * @param sdkInfo the sdkInfo object or null + * @param sdkVersion the SdkVersion object or null */ @ApiStatus.Internal - public void setSdkInfo(final @Nullable SdkInfo sdkInfo) { - this.sdkInfo = sdkInfo; + public void setSdkVersion(final @Nullable SdkVersion sdkVersion) { + this.sdkVersion = sdkVersion; } /** The BeforeSend callback */ diff --git a/sentry-core/src/main/java/io/sentry/core/cache/SessionCache.java b/sentry-core/src/main/java/io/sentry/core/cache/SessionCache.java index 41c075e3f..6cd8d6081 100644 --- a/sentry-core/src/main/java/io/sentry/core/cache/SessionCache.java +++ b/sentry-core/src/main/java/io/sentry/core/cache/SessionCache.java @@ -136,9 +136,9 @@ public void store(final @NotNull SentryEnvelope envelope, final @Nullable Object session.end(timestamp); // if the App. has been upgraded and there's a new version of the SDK running, - // SdkInfo will be outdated. + // SdkVersion will be outdated. final SentryEnvelope fromSession = - SentryEnvelope.fromSession(serializer, session, options.getSdkInfo()); + SentryEnvelope.fromSession(serializer, session, options.getSdkVersion()); final File fileFromSession = getEnvelopeFile(fromSession); writeEnvelopeToDisk(fileFromSession, fromSession); } diff --git a/sentry-core/src/main/java/io/sentry/core/protocol/SdkInfo.java b/sentry-core/src/main/java/io/sentry/core/protocol/SdkInfo.java index 373a1d5a5..892db05f0 100644 --- a/sentry-core/src/main/java/io/sentry/core/protocol/SdkInfo.java +++ b/sentry-core/src/main/java/io/sentry/core/protocol/SdkInfo.java @@ -1,10 +1,8 @@ package io.sentry.core.protocol; import io.sentry.core.IUnknownPropertiesConsumer; -import io.sentry.core.util.IntegerUtils; import java.util.Map; import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; public final class SdkInfo implements IUnknownPropertiesConsumer { private String sdkName; @@ -52,37 +50,4 @@ public void setVersionPatchlevel(Integer versionPatchlevel) { public void acceptUnknownProperties(Map unknown) { this.unknown = unknown; } - - /** - * Creates a SdkInfo object based on the given values - * - * @param sdkName the SdkName - * @param sdkVersion the Version String (semver) eg 1.2.3 - * @return the parsed SdkInfo object - */ - public static @NotNull SdkInfo createSdkInfo( - final @NotNull String sdkName, final @NotNull String sdkVersion) { - SdkInfo sdkInfo = new SdkInfo(); - sdkInfo.setSdkName(sdkName); - - String[] version = sdkVersion.split("\\.", -1); - if (version.length >= 1) { - sdkInfo.setVersionMajor(IntegerUtils.getNumber(version[0])); - - if (version.length >= 2) { - sdkInfo.setVersionMinor(IntegerUtils.getNumber(version[1])); - - if (version.length >= 3) { - String patch = version[2]; - // special casing for debug builds that appends a suffix eg: -SNAPSHOT - String[] patchAndSnapshot = patch.split("-", -1); - if (patchAndSnapshot.length >= 1) { - sdkInfo.setVersionPatchlevel(IntegerUtils.getNumber(patchAndSnapshot[0])); - } - } - } - } - - return sdkInfo; - } } diff --git a/sentry-core/src/main/java/io/sentry/core/protocol/SdkVersion.java b/sentry-core/src/main/java/io/sentry/core/protocol/SdkVersion.java index 69c764038..4621be33d 100644 --- a/sentry-core/src/main/java/io/sentry/core/protocol/SdkVersion.java +++ b/sentry-core/src/main/java/io/sentry/core/protocol/SdkVersion.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Map; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; public final class SdkVersion implements IUnknownPropertiesConsumer { private String name; @@ -53,4 +54,12 @@ public void addIntegration(String integration) { public void acceptUnknownProperties(Map unknown) { this.unknown = unknown; } + + public @Nullable List getPackages() { + return packages; + } + + public @Nullable List getIntegrations() { + return integrations; + } } diff --git a/sentry-core/src/main/java/io/sentry/core/util/IntegerUtils.java b/sentry-core/src/main/java/io/sentry/core/util/IntegerUtils.java deleted file mode 100644 index 0f4fe0919..000000000 --- a/sentry-core/src/main/java/io/sentry/core/util/IntegerUtils.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.sentry.core.util; - -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.Nullable; - -@ApiStatus.Internal -public final class IntegerUtils { - - private IntegerUtils() {} - - /** - * Try to parse a String to a Number - * - * @param number the number String - * @return the Integer or null - */ - public static @Nullable Integer getNumber(final @Nullable String number) { - if (number == null || number.isEmpty()) { - return null; - } - try { - return Integer.valueOf(number); - } catch (NumberFormatException ignored) { - // number String is not a number - } - return null; - } -} diff --git a/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt b/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt index 14ec769d0..1df86c032 100644 --- a/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt +++ b/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt @@ -18,7 +18,7 @@ import io.sentry.core.hints.DiskFlushNotification import io.sentry.core.hints.SessionEndHint import io.sentry.core.hints.SessionUpdateHint import io.sentry.core.protocol.Request -import io.sentry.core.protocol.SdkInfo +import io.sentry.core.protocol.SdkVersion import io.sentry.core.protocol.SentryException import io.sentry.core.protocol.SentryId import io.sentry.core.protocol.User @@ -42,7 +42,10 @@ class SentryClientTest { class Fixture { var sentryOptions: SentryOptions = SentryOptions().apply { dsn = dsnString - sdkInfo = SdkInfo.createSdkInfo("test", "1.2.3") + sdkVersion = SdkVersion().apply { + name = "test" + version = "1.2.3" + } } var connection: AsyncConnection = mock() fun getSut() = SentryClient(sentryOptions, connection) @@ -446,7 +449,7 @@ class SentryClientTest { fun `when captureSession, sdkInfo should be in the envelope header`() { fixture.getSut().captureSession(createSession()) verify(fixture.connection).send(check { - assertNotNull(it.header.sdkInfo) + assertNotNull(it.header.sdkVersion) }, anyOrNull()) } diff --git a/sentry-core/src/test/java/io/sentry/core/protocol/SdkInfoTest.kt b/sentry-core/src/test/java/io/sentry/core/protocol/SdkInfoTest.kt deleted file mode 100644 index 6c2acd1a7..000000000 --- a/sentry-core/src/test/java/io/sentry/core/protocol/SdkInfoTest.kt +++ /dev/null @@ -1,53 +0,0 @@ -package io.sentry.core.protocol - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class SdkInfoTest { - - @Test - fun `SdkInfo creates object with given values`() { - val sdkInfo = SdkInfo.createSdkInfo("test", "1.2.3") - assertEquals("test", sdkInfo.sdkName) - assertEquals(1, sdkInfo.versionMajor) - assertEquals(2, sdkInfo.versionMinor) - assertEquals(3, sdkInfo.versionPatchlevel) - } - - @Test - fun `SdkInfo creates object with given values but suffix`() { - val sdkInfo = SdkInfo.createSdkInfo("test", "1.2.3-SNAPSHOT") - assertEquals("test", sdkInfo.sdkName) - assertEquals(1, sdkInfo.versionMajor) - assertEquals(2, sdkInfo.versionMinor) - assertEquals(3, sdkInfo.versionPatchlevel) - } - - @Test - fun `SdkInfo creates object with given values but patch`() { - val sdkInfo = SdkInfo.createSdkInfo("test", "1.2") - assertEquals("test", sdkInfo.sdkName) - assertEquals(1, sdkInfo.versionMajor) - assertEquals(2, sdkInfo.versionMinor) - assertNull(sdkInfo.versionPatchlevel) - } - - @Test - fun `SdkInfo creates object with given values but minor`() { - val sdkInfo = SdkInfo.createSdkInfo("test", "1") - assertEquals("test", sdkInfo.sdkName) - assertEquals(1, sdkInfo.versionMajor) - assertNull(sdkInfo.versionMinor) - assertNull(sdkInfo.versionPatchlevel) - } - - @Test - fun `SdkInfo creates object with given values but no version`() { - val sdkInfo = SdkInfo.createSdkInfo("test", "") - assertEquals("test", sdkInfo.sdkName) - assertNull(sdkInfo.versionMajor) - assertNull(sdkInfo.versionMinor) - assertNull(sdkInfo.versionPatchlevel) - } -} diff --git a/sentry-core/src/test/java/io/sentry/core/util/IntegerUtilsTest.kt b/sentry-core/src/test/java/io/sentry/core/util/IntegerUtilsTest.kt deleted file mode 100644 index b457c9939..000000000 --- a/sentry-core/src/test/java/io/sentry/core/util/IntegerUtilsTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.sentry.core.util - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNull - -class IntegerUtilsTest { - - @Test - fun `Number String is converted to Integer`() { - assertEquals(1, IntegerUtils.getNumber("1")) - } - - @Test - fun `Empty String returns null`() { - assertNull(IntegerUtils.getNumber("")) - } - - @Test - fun `Null String returns null`() { - assertNull(IntegerUtils.getNumber(null)) - } - - @Test - fun `Non number String returns null`() { - assertNull(IntegerUtils.getNumber("abc")) - } -}