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 45862368a..d13098ee0 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 @@ -202,10 +202,6 @@ class AndroidSerializerTest { private fun generateEmptySentryEvent(): SentryEvent { return SentryEvent().apply { - setBreadcrumbs(null) - setTags(null) - setExtra(null) - fingerprint = null contexts = null } } 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 5b99de755..c52f0eba8 100644 --- a/sentry-core/src/main/java/io/sentry/core/SentryClient.java +++ b/sentry-core/src/main/java/io/sentry/core/SentryClient.java @@ -6,6 +6,9 @@ import io.sentry.core.transport.AsyncConnection; import io.sentry.core.util.Nullable; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; public class SentryClient implements ISentryClient { static final String SENTRY_PROTOCOL_VERSION = "7"; @@ -35,6 +38,45 @@ public SentryClient(SentryOptions options, @Nullable AsyncConnection connection) public SentryId captureEvent(SentryEvent event, @Nullable Scope scope) { log(options.getLogger(), SentryLevel.DEBUG, "Capturing event: %s", event.getEventId()); + if (scope != null) { + if (event.getTransaction() == null) { + event.setTransaction(scope.getTransaction()); + } + if (event.getUser() == null) { + event.setUser(scope.getUser()); + } + if (event.getFingerprint() == null) { + event.setFingerprint(scope.getFingerprint()); + } + if (event.getBreadcrumbs() == null) { + event.setBreadcrumbs(new ArrayList<>(scope.getBreadcrumbs())); + } else { + event.getBreadcrumbs().addAll(scope.getBreadcrumbs()); + } + if (event.getTags() == null) { + event.setTags(new HashMap<>(scope.getTags())); + } else { + for (Map.Entry item : scope.getTags().entrySet()) { + if (!event.getTags().containsKey(item.getKey())) { + event.getTags().put(item.getKey(), item.getValue()); + } + } + } + if (event.getExtra() == null) { + event.setExtra(new HashMap<>(scope.getExtra())); + } else { + for (Map.Entry item : scope.getExtra().entrySet()) { + if (!event.getExtra().containsKey(item.getKey())) { + event.getExtra().put(item.getKey(), item.getValue()); + } + } + } + // Level from scope exceptionally take precedence over the event + if (scope.getLevel() != null) { + event.setLevel(scope.getLevel()); + } + } + for (EventProcessor processor : options.getEventProcessors()) { processor.process(event); } diff --git a/sentry-core/src/main/java/io/sentry/core/SentryEvent.java b/sentry-core/src/main/java/io/sentry/core/SentryEvent.java index 5d729f1f2..9d667eb59 100644 --- a/sentry-core/src/main/java/io/sentry/core/SentryEvent.java +++ b/sentry-core/src/main/java/io/sentry/core/SentryEvent.java @@ -27,10 +27,10 @@ public class SentryEvent implements IUnknownPropertiesConsumer { private Request request; private SdkVersion sdk; private Contexts contexts = new Contexts(); - private List fingerprint = new ArrayList<>(); - private List breadcrumbs = new ArrayList<>(); - private Map tags = new HashMap<>(); - private Map extra = new HashMap<>(); + private List fingerprint; + private List breadcrumbs; + private Map tags; + private Map extra; private Map unknown; SentryEvent(SentryId eventId, Date timestamp) { @@ -199,26 +199,47 @@ public List getBreadcrumbs() { return breadcrumbs; } - public void setBreadcrumbs(ArrayList breadcrumbs) { + public void setBreadcrumbs(List breadcrumbs) { this.breadcrumbs = breadcrumbs; } + public void addBreadcrumb(Breadcrumb breadcrumb) { + if (breadcrumbs == null) { + breadcrumbs = new ArrayList<>(); + } + breadcrumbs.add(breadcrumb); + } + public Map getTags() { return tags; } - public void setTags(HashMap tags) { + public void setTags(Map tags) { this.tags = tags; } + public void setTag(String key, String value) { + if (tags == null) { + tags = new HashMap<>(); + } + tags.put(key, value); + } + public Map getExtra() { return extra; } - public void setExtra(HashMap extra) { + public void setExtra(Map extra) { this.extra = extra; } + public void setExtra(String key, Object value) { + if (extra == null) { + extra = new HashMap<>(); + } + extra.put(key, value); + } + public Contexts getContexts() { return contexts; } 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 3123639d2..08b76e530 100644 --- a/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt +++ b/sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt @@ -4,6 +4,7 @@ import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.times import com.nhaarman.mockitokotlin2.verify +import io.sentry.core.protocol.User import io.sentry.core.transport.AsyncConnection import kotlin.test.Ignore import kotlin.test.Test @@ -156,4 +157,117 @@ class SentryClientTest { sut.captureEvent(event) assertEquals(expected, event.environment) } + + @Test + fun `when captureEvent with scope, event should have its data if not set`() { + val event = SentryEvent() + val scope = createScope() + + val sut = fixture.getSut() + + sut.captureEvent(event, scope) + assertEquals("message", event.breadcrumbs[0].message) + assertEquals("extra", event.extra["extra"]) + assertEquals("tags", event.tags["tags"]) + assertEquals("fp", event.fingerprint[0]) + assertEquals("transaction", event.transaction) + assertEquals("id", event.user.id) + assertEquals(SentryLevel.FATAL, event.level) + } + + @Test + fun `when captureEvent with scope, event data has priority over scope but level and it should append extras, tags and breadcrumbs`() { + val event = createEvent() + + val scope = createScope() + + val sut = fixture.getSut() + + sut.captureEvent(event, scope) + + // breadcrumbs are appending + assertEquals("eventMessage", event.breadcrumbs[0].message) + assertEquals("message", event.breadcrumbs[1].message) + + // extras are appending + assertEquals("eventExtra", event.extra["eventExtra"]) + assertEquals("extra", event.extra["extra"]) + + // tags are appending + assertEquals("eventTag", event.tags["eventTag"]) + assertEquals("tags", event.tags["tags"]) + + // fingerprint is replaced + assertEquals("eventFp", event.fingerprint[0]) + assertEquals(1, event.fingerprint.size) + + assertEquals("eventTransaction", event.transaction) + + assertEquals("eventId", event.user.id) + + assertEquals(SentryLevel.FATAL, event.level) + } + + @Test + fun `when captureEvent with scope, event extras and tags are only append if key is absent`() { + val event = createEvent() + + val scope = createScope() + scope.setExtra("eventExtra", "extra") + scope.setTag("eventTag", "tags") + + val sut = fixture.getSut() + + sut.captureEvent(event, scope) + + // extras are appending + assertEquals("eventExtra", event.extra["eventExtra"]) + + // tags are appending + assertEquals("eventTag", event.tags["eventTag"]) + } + + @Test + fun `when captureEvent with scope, event should have its level if set`() { + val event = SentryEvent() + event.level = SentryLevel.DEBUG + val scope = createScope() + + val sut = fixture.getSut() + + sut.captureEvent(event, scope) + assertEquals(SentryLevel.FATAL, event.level) + } + + private fun createScope(): Scope { + return Scope().apply { + addBreadcrumb(Breadcrumb().apply { + message = "message" + }) + setExtra("extra", "extra") + setTag("tags", "tags") + fingerprint.add("fp") + transaction = "transaction" + level = SentryLevel.FATAL + user = User().apply { + id = "id" + } + } + } + + private fun createEvent(): SentryEvent { + return SentryEvent().apply { + addBreadcrumb(Breadcrumb().apply { + message = "eventMessage" + }) + setExtra("eventExtra", "eventExtra") + setTag("eventTag", "eventTag") + fingerprint = listOf("eventFp") + transaction = "eventTransaction" + level = SentryLevel.DEBUG + user = User().apply { + id = "eventId" + } + } + } }