diff --git a/CHANGELOG.md b/CHANGELOG.md index d4574a0fceb..d6ddff9da8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,14 @@ ## Unreleased -### Behavioural Changes - -- Use `java.net.URI` for parsing URLs in `UrlUtils` ([#4210](https://github.com/getsentry/sentry-java/pull/4210)) - - This could affect grouping for issues with messages containing URLs that fall in known corner cases that were handled incorrectly previously (e.g. email in URL path) - ### Fixes +- The SDK now handles `null` on many APIs instead of expecting a non `null` value ([#4245](https://github.com/getsentry/sentry-java/pull/4245)) + - Certain APIs like `setTag`, `setData`, `setExtra`, `setContext` previously caused a `NullPointerException` when invoked with either `null` key or value. + - The SDK now tries to have a sane fallback when `null` is passed and no longer throws `NullPointerException` + - If `null` is passed, the SDK will + - do nothing if a `null` key is passed, returning `null` for non void methods + - remove any previous value if the new value is set to `null` - Add support for setting in-app-includes/in-app-excludes via AndroidManifest.xml ([#4240](https://github.com/getsentry/sentry-java/pull/4240)) - Modifications to OkHttp requests are now properly propagated to the affected span / breadcrumbs ([#4238](https://github.com/getsentry/sentry-java/pull/4238)) - Please ensure the SentryOkHttpInterceptor is added last to your OkHttpClient, as otherwise changes to the `Request` by subsequent interceptors won't be considered @@ -22,6 +23,11 @@ - Set `sentry.capture-open-telemetry-events=true` in Springs `application.properties` to enable it - Set `sentry.captureOpenTelemetryEvents: true` in Springs `application.yml` to enable it +### Behavioural Changes + +- Use `java.net.URI` for parsing URLs in `UrlUtils` ([#4210](https://github.com/getsentry/sentry-java/pull/4210)) + - This could affect grouping for issues with messages containing URLs that fall in known corner cases that were handled incorrectly previously (e.g. email in URL path) + ### Internal - Also use port when checking if a request is made to Sentry DSN ([#4231](https://github.com/getsentry/sentry-java/pull/4231)) diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt index 7ddabb84ea7..58468fc59ca 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/InternalSentrySdkTest.kt @@ -319,7 +319,7 @@ class InternalSentrySdkTest { fun `serializeScope provides fallback app data if none is set`() { val options = SentryAndroidOptions() val scope = Scope(options) - scope.setContexts("app", null) + scope.setContexts("app", null as Any?) val serializedScope = InternalSentrySdk.serializeScope(context, options, scope) assertTrue(((serializedScope["contexts"] as Map<*, *>)["app"] as Map<*, *>).containsKey("app_name")) diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStrongRefSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStrongRefSpanWrapper.java index 7f026742e9f..a4008a01283 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStrongRefSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelStrongRefSpanWrapper.java @@ -225,12 +225,12 @@ public void setThrowable(@Nullable Throwable throwable) { } @Override - public void setTag(@NotNull String key, @NotNull String value) { + public void setTag(@Nullable String key, @Nullable String value) { delegate.setTag(key, value); } @Override - public @Nullable String getTag(@NotNull String key) { + public @Nullable String getTag(@Nullable String key) { return delegate.getTag(key); } @@ -240,12 +240,12 @@ public boolean isFinished() { } @Override - public void setData(@NotNull String key, @NotNull Object value) { + public void setData(@Nullable String key, @Nullable Object value) { delegate.setData(key, value); } @Override - public @Nullable Object getData(@NotNull String key) { + public @Nullable Object getData(@Nullable String key) { return delegate.getData(key); } @@ -281,7 +281,7 @@ public boolean isNoOp() { } @Override - public void setContext(@NotNull String key, @NotNull Object context) { + public void setContext(@Nullable String key, @Nullable Object context) { delegate.setContext(key, context); } diff --git a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java index 18e5d1b6db1..7d0618af040 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java +++ b/sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/OtelTransactionSpanForwarder.java @@ -152,12 +152,12 @@ public void setThrowable(@Nullable Throwable throwable) { } @Override - public void setTag(@NotNull String key, @NotNull String value) { + public void setTag(@Nullable String key, @Nullable String value) { rootSpan.setTag(key, value); } @Override - public @Nullable String getTag(@NotNull String key) { + public @Nullable String getTag(@Nullable String key) { return rootSpan.getTag(key); } @@ -167,12 +167,12 @@ public boolean isFinished() { } @Override - public void setData(@NotNull String key, @NotNull Object value) { + public void setData(@Nullable String key, @Nullable Object value) { rootSpan.setData(key, value); } @Override - public @Nullable Object getData(@NotNull String key) { + public @Nullable Object getData(@Nullable String key) { return rootSpan.getData(key); } @@ -277,7 +277,7 @@ public void finish( } @Override - public void setContext(@NotNull String key, @NotNull Object context) { + public void setContext(@Nullable String key, @Nullable Object context) { // thoughts: // - span would have to save it on global storage too since we can't add complex data to otel // span diff --git a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java index 8d11cb8b772..bc78643fb9b 100644 --- a/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java +++ b/sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/OtelSpanWrapper.java @@ -330,12 +330,15 @@ public void setThrowable(@Nullable Throwable throwable) { } @Override - public void setTag(@NotNull String key, @NotNull String value) { + public void setTag(@Nullable String key, @Nullable String value) { context.setTag(key, value); } @Override - public @Nullable String getTag(@NotNull String key) { + public @Nullable String getTag(@Nullable String key) { + if (key == null) { + return null; + } return context.getTags().get(key); } @@ -357,12 +360,22 @@ public boolean isFinished() { } @Override - public void setData(@NotNull String key, @NotNull Object value) { - data.put(key, value); + public void setData(@Nullable String key, @Nullable Object value) { + if (key == null) { + return; + } + if (value == null) { + data.remove(key); + } else { + data.put(key, value); + } } @Override - public @Nullable Object getData(@NotNull String key) { + public @Nullable Object getData(@Nullable String key) { + if (key == null) { + return null; + } return data.get(key); } @@ -422,7 +435,7 @@ public boolean isNoOp() { } @Override - public void setContext(@NotNull String key, @NotNull Object context) { + public void setContext(@Nullable String key, @Nullable Object context) { contexts.put(key, context); } diff --git a/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/LoggingInsecureRestClient.kt b/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/LoggingInsecureRestClient.kt index e1773f1c0e7..72dcc34f5f6 100644 --- a/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/LoggingInsecureRestClient.kt +++ b/sentry-system-test-support/src/main/kotlin/io/sentry/systemtest/util/LoggingInsecureRestClient.kt @@ -10,6 +10,7 @@ import okhttp3.RequestBody import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import org.slf4j.LoggerFactory +import java.util.concurrent.TimeUnit open class LoggingInsecureRestClient { val logger = LoggerFactory.getLogger(LoggingInsecureRestClient::class.java) @@ -55,6 +56,10 @@ open class LoggingInsecureRestClient { protected fun client(): OkHttpClient { return OkHttpClient.Builder() + .callTimeout(60, TimeUnit.SECONDS) + .connectTimeout(60, TimeUnit.SECONDS) + .readTimeout(60, TimeUnit.SECONDS) + .writeTimeout(60, TimeUnit.SECONDS) .build() } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index fb29473c4ac..fef0f23bfd6 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -238,8 +238,11 @@ public final class io/sentry/CombinedContextsView : io/sentry/protocol/Contexts public fun isEmpty ()Z public fun keys ()Ljava/util/Enumeration; public fun put (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; + public fun putAll (Lio/sentry/protocol/Contexts;)V + public fun putAll (Ljava/util/Map;)V public fun remove (Ljava/lang/Object;)Ljava/lang/Object; public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public fun set (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object; public fun setApp (Lio/sentry/protocol/App;)V public fun setBrowser (Lio/sentry/protocol/Browser;)V public fun setDevice (Lio/sentry/protocol/Device;)V diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index d5096455108..d5bb66d33e1 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -616,7 +616,10 @@ public Map getData() { * @return the value or null */ @Nullable - public Object getData(final @NotNull String key) { + public Object getData(final @Nullable String key) { + if (key == null) { + return null; + } return data.get(key); } @@ -626,8 +629,15 @@ public Object getData(final @NotNull String key) { * @param key the key * @param value the value */ - public void setData(@NotNull String key, @NotNull Object value) { - data.put(key, value); + public void setData(@Nullable String key, @Nullable Object value) { + if (key == null) { + return; + } + if (value == null) { + removeData(key); + } else { + data.put(key, value); + } } /** @@ -635,7 +645,10 @@ public void setData(@NotNull String key, @NotNull Object value) { * * @param key the key */ - public void removeData(@NotNull String key) { + public void removeData(@Nullable String key) { + if (key == null) { + return; + } data.remove(key); } diff --git a/sentry/src/main/java/io/sentry/CombinedContextsView.java b/sentry/src/main/java/io/sentry/CombinedContextsView.java index 11e459877d6..31b5c060620 100644 --- a/sentry/src/main/java/io/sentry/CombinedContextsView.java +++ b/sentry/src/main/java/io/sentry/CombinedContextsView.java @@ -241,14 +241,14 @@ public boolean isEmpty() { } @Override - public boolean containsKey(final @NotNull Object key) { + public boolean containsKey(final @Nullable Object key) { return globalContexts.containsKey(key) || isolationContexts.containsKey(key) || currentContexts.containsKey(key); } @Override - public @Nullable Object get(final @NotNull Object key) { + public @Nullable Object get(final @Nullable Object key) { final @Nullable Object current = currentContexts.get(key); if (current != null) { return current; @@ -261,12 +261,12 @@ public boolean containsKey(final @NotNull Object key) { } @Override - public @Nullable Object put(final @NotNull String key, final @Nullable Object value) { + public @Nullable Object put(final @Nullable String key, final @Nullable Object value) { return getDefaultContexts().put(key, value); } @Override - public @Nullable Object remove(final @NotNull Object key) { + public @Nullable Object remove(final @Nullable Object key) { return getDefaultContexts().remove(key); } @@ -281,10 +281,26 @@ public boolean containsKey(final @NotNull Object key) { } @Override - public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) throws IOException { + public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { mergeContexts().serialize(writer, logger); } + @Override + public @Nullable Object set(@Nullable String key, @Nullable Object value) { + return put(key, value); + } + + @Override + public void putAll(@Nullable Map m) { + getDefaultContexts().putAll(m); + } + + @Override + public void putAll(@Nullable Contexts contexts) { + getDefaultContexts().putAll(contexts); + } + private @NotNull Contexts mergeContexts() { final @NotNull Contexts allContexts = new Contexts(); allContexts.putAll(globalContexts); diff --git a/sentry/src/main/java/io/sentry/CombinedScopeView.java b/sentry/src/main/java/io/sentry/CombinedScopeView.java index 129066450f3..d6ac5b824a9 100644 --- a/sentry/src/main/java/io/sentry/CombinedScopeView.java +++ b/sentry/src/main/java/io/sentry/CombinedScopeView.java @@ -229,12 +229,12 @@ public void clear() { } @Override - public void setTag(@NotNull String key, @NotNull String value) { + public void setTag(@Nullable String key, @Nullable String value) { getDefaultWriteScope().setTag(key, value); } @Override - public void removeTag(@NotNull String key) { + public void removeTag(@Nullable String key) { getDefaultWriteScope().removeTag(key); } @@ -248,12 +248,12 @@ public void removeTag(@NotNull String key) { } @Override - public void setExtra(@NotNull String key, @NotNull String value) { + public void setExtra(@Nullable String key, @Nullable String value) { getDefaultWriteScope().setExtra(key, value); } @Override - public void removeExtra(@NotNull String key) { + public void removeExtra(@Nullable String key) { getDefaultWriteScope().removeExtra(key); } @@ -267,42 +267,42 @@ public void removeExtra(@NotNull String key) { } @Override - public void setContexts(@NotNull String key, @NotNull Object value) { + public void setContexts(@Nullable String key, @Nullable Object value) { getDefaultWriteScope().setContexts(key, value); } @Override - public void setContexts(@NotNull String key, @NotNull Boolean value) { + public void setContexts(@Nullable String key, @Nullable Boolean value) { getDefaultWriteScope().setContexts(key, value); } @Override - public void setContexts(@NotNull String key, @NotNull String value) { + public void setContexts(@Nullable String key, @Nullable String value) { getDefaultWriteScope().setContexts(key, value); } @Override - public void setContexts(@NotNull String key, @NotNull Number value) { + public void setContexts(@Nullable String key, @Nullable Number value) { getDefaultWriteScope().setContexts(key, value); } @Override - public void setContexts(@NotNull String key, @NotNull Collection value) { + public void setContexts(@Nullable String key, @Nullable Collection value) { getDefaultWriteScope().setContexts(key, value); } @Override - public void setContexts(@NotNull String key, @NotNull Object[] value) { + public void setContexts(@Nullable String key, @Nullable Object[] value) { getDefaultWriteScope().setContexts(key, value); } @Override - public void setContexts(@NotNull String key, @NotNull Character value) { + public void setContexts(@Nullable String key, @Nullable Character value) { getDefaultWriteScope().setContexts(key, value); } @Override - public void removeContexts(@NotNull String key) { + public void removeContexts(@Nullable String key) { getDefaultWriteScope().removeContexts(key); } diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index fc2f9c15dcd..7fba4da99d8 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -128,22 +128,22 @@ public void clearBreadcrumbs() { } @Override - public void setTag(@NotNull String key, @NotNull String value) { + public void setTag(@Nullable String key, @Nullable String value) { Sentry.setTag(key, value); } @Override - public void removeTag(@NotNull String key) { + public void removeTag(@Nullable String key) { Sentry.removeTag(key); } @Override - public void setExtra(@NotNull String key, @NotNull String value) { + public void setExtra(@Nullable String key, @Nullable String value) { Sentry.setExtra(key, value); } @Override - public void removeExtra(@NotNull String key) { + public void removeExtra(@Nullable String key) { Sentry.removeExtra(key); } diff --git a/sentry/src/main/java/io/sentry/HubScopesWrapper.java b/sentry/src/main/java/io/sentry/HubScopesWrapper.java index 591852a9adf..9ca84df9a17 100644 --- a/sentry/src/main/java/io/sentry/HubScopesWrapper.java +++ b/sentry/src/main/java/io/sentry/HubScopesWrapper.java @@ -123,22 +123,22 @@ public void clearBreadcrumbs() { } @Override - public void setTag(@NotNull String key, @NotNull String value) { + public void setTag(@Nullable String key, @Nullable String value) { scopes.setTag(key, value); } @Override - public void removeTag(@NotNull String key) { + public void removeTag(@Nullable String key) { scopes.removeTag(key); } @Override - public void setExtra(@NotNull String key, @NotNull String value) { + public void setExtra(@Nullable String key, @Nullable String value) { scopes.setExtra(key, value); } @Override - public void removeExtra(@NotNull String key) { + public void removeExtra(@Nullable String key) { scopes.removeExtra(key); } diff --git a/sentry/src/main/java/io/sentry/IScope.java b/sentry/src/main/java/io/sentry/IScope.java index 3c7c1ecca64..ddabd00569e 100644 --- a/sentry/src/main/java/io/sentry/IScope.java +++ b/sentry/src/main/java/io/sentry/IScope.java @@ -196,14 +196,14 @@ public interface IScope { * @param key the key * @param value the value */ - void setTag(final @NotNull String key, final @NotNull String value); + void setTag(final @Nullable String key, final @Nullable String value); /** * Removes a tag from the Scope's tags * * @param key the key */ - void removeTag(final @NotNull String key); + void removeTag(final @Nullable String key); /** * Returns the Scope's extra map @@ -220,14 +220,14 @@ public interface IScope { * @param key the key * @param value the value */ - void setExtra(final @NotNull String key, final @NotNull String value); + void setExtra(final @Nullable String key, final @Nullable String value); /** * Removes an extra from the Scope's extras * * @param key the key */ - void removeExtra(final @NotNull String key); + void removeExtra(final @Nullable String key); /** * Returns the Scope's contexts @@ -243,7 +243,7 @@ public interface IScope { * @param key the context key * @param value the context value */ - void setContexts(final @NotNull String key, final @NotNull Object value); + void setContexts(final @Nullable String key, final @Nullable Object value); /** * Sets the Scope's contexts @@ -251,7 +251,7 @@ public interface IScope { * @param key the context key * @param value the context value */ - void setContexts(final @NotNull String key, final @NotNull Boolean value); + void setContexts(final @Nullable String key, final @Nullable Boolean value); /** * Sets the Scope's contexts @@ -259,7 +259,7 @@ public interface IScope { * @param key the context key * @param value the context value */ - void setContexts(final @NotNull String key, final @NotNull String value); + void setContexts(final @Nullable String key, final @Nullable String value); /** * Sets the Scope's contexts @@ -267,7 +267,7 @@ public interface IScope { * @param key the context key * @param value the context value */ - void setContexts(final @NotNull String key, final @NotNull Number value); + void setContexts(final @Nullable String key, final @Nullable Number value); /** * Sets the Scope's contexts @@ -275,7 +275,7 @@ public interface IScope { * @param key the context key * @param value the context value */ - void setContexts(final @NotNull String key, final @NotNull Collection value); + void setContexts(final @Nullable String key, final @Nullable Collection value); /** * Sets the Scope's contexts @@ -283,7 +283,7 @@ public interface IScope { * @param key the context key * @param value the context value */ - void setContexts(final @NotNull String key, final @NotNull Object[] value); + void setContexts(final @Nullable String key, final @Nullable Object[] value); /** * Sets the Scope's contexts @@ -291,14 +291,14 @@ public interface IScope { * @param key the context key * @param value the context value */ - void setContexts(final @NotNull String key, final @NotNull Character value); + void setContexts(final @Nullable String key, final @Nullable Character value); /** * Removes a value from the Scope's contexts * * @param key the Key */ - void removeContexts(final @NotNull String key); + void removeContexts(final @Nullable String key); /** * Returns the Scopes's attachments diff --git a/sentry/src/main/java/io/sentry/IScopes.java b/sentry/src/main/java/io/sentry/IScopes.java index e07de9c327c..c93f558ea57 100644 --- a/sentry/src/main/java/io/sentry/IScopes.java +++ b/sentry/src/main/java/io/sentry/IScopes.java @@ -271,14 +271,14 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * @param key the key * @param value the value */ - void setTag(@NotNull String key, @NotNull String value); + void setTag(@Nullable String key, @Nullable String value); /** * Removes the tag to a string value to the current Scope * * @param key the key */ - void removeTag(@NotNull String key); + void removeTag(@Nullable String key); /** * Sets the extra key to an arbitrary value to the current Scope, overwriting a potential previous @@ -287,14 +287,14 @@ default void addBreadcrumb(@NotNull String message, @NotNull String category) { * @param key the key * @param value the value */ - void setExtra(@NotNull String key, @NotNull String value); + void setExtra(@Nullable String key, @Nullable String value); /** * Removes the extra key to an arbitrary value to the current Scope * * @param key the key */ - void removeExtra(@NotNull String key); + void removeExtra(@Nullable String key); /** * Last event id recorded in the current scope diff --git a/sentry/src/main/java/io/sentry/ISpan.java b/sentry/src/main/java/io/sentry/ISpan.java index f915f60cb5d..0765f5127f9 100644 --- a/sentry/src/main/java/io/sentry/ISpan.java +++ b/sentry/src/main/java/io/sentry/ISpan.java @@ -171,10 +171,10 @@ ISpan startChild( * @param key the tag key * @param value the tag value */ - void setTag(@NotNull String key, @NotNull String value); + void setTag(@Nullable String key, @Nullable String value); @Nullable - String getTag(@NotNull String key); + String getTag(@Nullable String key); /** * Returns if span has finished. @@ -189,7 +189,7 @@ ISpan startChild( * @param key the data key * @param value the data value */ - void setData(@NotNull String key, @NotNull Object value); + void setData(@Nullable String key, @Nullable Object value); /** * Returns extra data from span or transaction. @@ -197,7 +197,7 @@ ISpan startChild( * @return the data */ @Nullable - Object getData(@NotNull String key); + Object getData(@Nullable String key); /** * Set a measurement without unit. When setting the measurement without the unit, no formatting @@ -260,7 +260,7 @@ ISpan startChild( @ApiStatus.Internal boolean isNoOp(); - void setContext(@NotNull String key, @NotNull Object context); + void setContext(@Nullable String key, @Nullable Object context); @NotNull Contexts getContexts(); diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index d3e0b010c39..9b39ce77a6d 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -106,16 +106,16 @@ public void setFingerprint(@NotNull List fingerprint) {} public void clearBreadcrumbs() {} @Override - public void setTag(@NotNull String key, @NotNull String value) {} + public void setTag(@Nullable String key, @Nullable String value) {} @Override - public void removeTag(@NotNull String key) {} + public void removeTag(@Nullable String key) {} @Override - public void setExtra(@NotNull String key, @NotNull String value) {} + public void setExtra(@Nullable String key, @Nullable String value) {} @Override - public void removeExtra(@NotNull String key) {} + public void removeExtra(@Nullable String key) {} @Override public @NotNull SentryId getLastEventId() { diff --git a/sentry/src/main/java/io/sentry/NoOpScope.java b/sentry/src/main/java/io/sentry/NoOpScope.java index d5c1b56d8cf..d996fa29d59 100644 --- a/sentry/src/main/java/io/sentry/NoOpScope.java +++ b/sentry/src/main/java/io/sentry/NoOpScope.java @@ -131,10 +131,10 @@ public void clear() {} } @Override - public void setTag(@NotNull String key, @NotNull String value) {} + public void setTag(@Nullable String key, @Nullable String value) {} @Override - public void removeTag(@NotNull String key) {} + public void removeTag(@Nullable String key) {} @ApiStatus.Internal @Override @@ -143,10 +143,10 @@ public void removeTag(@NotNull String key) {} } @Override - public void setExtra(@NotNull String key, @NotNull String value) {} + public void setExtra(@Nullable String key, @Nullable String value) {} @Override - public void removeExtra(@NotNull String key) {} + public void removeExtra(@Nullable String key) {} @Override public @NotNull Contexts getContexts() { @@ -154,28 +154,28 @@ public void removeExtra(@NotNull String key) {} } @Override - public void setContexts(@NotNull String key, @NotNull Object value) {} + public void setContexts(@Nullable String key, @Nullable Object value) {} @Override - public void setContexts(@NotNull String key, @NotNull Boolean value) {} + public void setContexts(@Nullable String key, @Nullable Boolean value) {} @Override - public void setContexts(@NotNull String key, @NotNull String value) {} + public void setContexts(@Nullable String key, @Nullable String value) {} @Override - public void setContexts(@NotNull String key, @NotNull Number value) {} + public void setContexts(@Nullable String key, @Nullable Number value) {} @Override - public void setContexts(@NotNull String key, @NotNull Collection value) {} + public void setContexts(@Nullable String key, @Nullable Collection value) {} @Override - public void setContexts(@NotNull String key, @NotNull Object[] value) {} + public void setContexts(@Nullable String key, @Nullable Object[] value) {} @Override - public void setContexts(@NotNull String key, @NotNull Character value) {} + public void setContexts(@Nullable String key, @Nullable Character value) {} @Override - public void removeContexts(@NotNull String key) {} + public void removeContexts(@Nullable String key) {} @ApiStatus.Internal @Override diff --git a/sentry/src/main/java/io/sentry/NoOpScopes.java b/sentry/src/main/java/io/sentry/NoOpScopes.java index 8255569387d..a37a730b74c 100644 --- a/sentry/src/main/java/io/sentry/NoOpScopes.java +++ b/sentry/src/main/java/io/sentry/NoOpScopes.java @@ -101,16 +101,16 @@ public void setFingerprint(@NotNull List fingerprint) {} public void clearBreadcrumbs() {} @Override - public void setTag(@NotNull String key, @NotNull String value) {} + public void setTag(@Nullable String key, @Nullable String value) {} @Override - public void removeTag(@NotNull String key) {} + public void removeTag(@Nullable String key) {} @Override - public void setExtra(@NotNull String key, @NotNull String value) {} + public void setExtra(@Nullable String key, @Nullable String value) {} @Override - public void removeExtra(@NotNull String key) {} + public void removeExtra(@Nullable String key) {} @Override public @NotNull SentryId getLastEventId() { diff --git a/sentry/src/main/java/io/sentry/NoOpSpan.java b/sentry/src/main/java/io/sentry/NoOpSpan.java index ef96d23de66..669e28cfcfa 100644 --- a/sentry/src/main/java/io/sentry/NoOpSpan.java +++ b/sentry/src/main/java/io/sentry/NoOpSpan.java @@ -120,10 +120,10 @@ public void setThrowable(@Nullable Throwable throwable) {} } @Override - public void setTag(@NotNull String key, @NotNull String value) {} + public void setTag(@Nullable String key, @Nullable String value) {} @Override - public @Nullable String getTag(@NotNull String key) { + public @Nullable String getTag(@Nullable String key) { return null; } @@ -133,10 +133,10 @@ public boolean isFinished() { } @Override - public void setData(@NotNull String key, @NotNull Object value) {} + public void setData(@Nullable String key, @Nullable Object value) {} @Override - public @Nullable Object getData(@NotNull String key) { + public @Nullable Object getData(@Nullable String key) { return null; } @@ -168,7 +168,7 @@ public boolean isNoOp() { } @Override - public void setContext(@NotNull String key, @NotNull Object context) {} + public void setContext(@Nullable String key, @Nullable Object context) {} @Override public @NotNull Contexts getContexts() { diff --git a/sentry/src/main/java/io/sentry/NoOpTransaction.java b/sentry/src/main/java/io/sentry/NoOpTransaction.java index 963ffac8f57..4d266d1952a 100644 --- a/sentry/src/main/java/io/sentry/NoOpTransaction.java +++ b/sentry/src/main/java/io/sentry/NoOpTransaction.java @@ -185,10 +185,10 @@ public void setThrowable(@Nullable Throwable throwable) {} } @Override - public void setTag(@NotNull String key, @NotNull String value) {} + public void setTag(@Nullable String key, @Nullable String value) {} @Override - public @Nullable String getTag(@NotNull String key) { + public @Nullable String getTag(@Nullable String key) { return null; } @@ -208,10 +208,10 @@ public void setTag(@NotNull String key, @NotNull String value) {} } @Override - public void setData(@NotNull String key, @NotNull Object value) {} + public void setData(@Nullable String key, @Nullable Object value) {} @Override - public @Nullable Object getData(@NotNull String key) { + public @Nullable Object getData(@Nullable String key) { return null; } @@ -224,7 +224,7 @@ public void setMeasurement( @ApiStatus.Internal @Override - public void setContext(@NotNull String key, @NotNull Object context) {} + public void setContext(@Nullable String key, @Nullable Object context) {} @ApiStatus.Internal @Override diff --git a/sentry/src/main/java/io/sentry/Scope.java b/sentry/src/main/java/io/sentry/Scope.java index 4dacc8e4dac..a3046dad28c 100644 --- a/sentry/src/main/java/io/sentry/Scope.java +++ b/sentry/src/main/java/io/sentry/Scope.java @@ -570,12 +570,19 @@ public void clear() { * @param value the value */ @Override - public void setTag(final @NotNull String key, final @NotNull String value) { - this.tags.put(key, value); + public void setTag(final @Nullable String key, final @Nullable String value) { + if (key == null) { + return; + } + if (value == null) { + removeTag(key); + } else { + this.tags.put(key, value); - for (final IScopeObserver observer : options.getScopeObservers()) { - observer.setTag(key, value); - observer.setTags(tags); + for (final IScopeObserver observer : options.getScopeObservers()) { + observer.setTag(key, value); + observer.setTags(tags); + } } } @@ -585,7 +592,10 @@ public void setTag(final @NotNull String key, final @NotNull String value) { * @param key the key */ @Override - public void removeTag(final @NotNull String key) { + public void removeTag(final @Nullable String key) { + if (key == null) { + return; + } this.tags.remove(key); for (final IScopeObserver observer : options.getScopeObservers()) { @@ -613,12 +623,19 @@ public Map getExtras() { * @param value the value */ @Override - public void setExtra(final @NotNull String key, final @NotNull String value) { - this.extra.put(key, value); + public void setExtra(final @Nullable String key, final @Nullable String value) { + if (key == null) { + return; + } + if (value == null) { + removeExtra(key); + } else { + this.extra.put(key, value); - for (final IScopeObserver observer : options.getScopeObservers()) { - observer.setExtra(key, value); - observer.setExtras(extra); + for (final IScopeObserver observer : options.getScopeObservers()) { + observer.setExtra(key, value); + observer.setExtras(extra); + } } } @@ -628,7 +645,10 @@ public void setExtra(final @NotNull String key, final @NotNull String value) { * @param key the key */ @Override - public void removeExtra(final @NotNull String key) { + public void removeExtra(final @Nullable String key) { + if (key == null) { + return; + } this.extra.remove(key); for (final IScopeObserver observer : options.getScopeObservers()) { @@ -654,7 +674,10 @@ public void removeExtra(final @NotNull String key) { * @param value the context value */ @Override - public void setContexts(final @NotNull String key, final @NotNull Object value) { + public void setContexts(final @Nullable String key, final @Nullable Object value) { + if (key == null) { + return; + } this.contexts.put(key, value); for (final IScopeObserver observer : options.getScopeObservers()) { @@ -669,10 +692,18 @@ public void setContexts(final @NotNull String key, final @NotNull Object value) * @param value the context value */ @Override - public void setContexts(final @NotNull String key, final @NotNull Boolean value) { - final Map map = new HashMap<>(); - map.put("value", value); - setContexts(key, map); + public void setContexts(final @Nullable String key, final @Nullable Boolean value) { + if (key == null) { + return; + } + if (value == null) { + // unset + setContexts(key, (Object) null); + } else { + final Map map = new HashMap<>(); + map.put("value", value); + setContexts(key, map); + } } /** @@ -682,10 +713,18 @@ public void setContexts(final @NotNull String key, final @NotNull Boolean value) * @param value the context value */ @Override - public void setContexts(final @NotNull String key, final @NotNull String value) { - final Map map = new HashMap<>(); - map.put("value", value); - setContexts(key, map); + public void setContexts(final @Nullable String key, final @Nullable String value) { + if (key == null) { + return; + } + if (value == null) { + // unset + setContexts(key, (Object) null); + } else { + final Map map = new HashMap<>(); + map.put("value", value); + setContexts(key, map); + } } /** @@ -695,10 +734,18 @@ public void setContexts(final @NotNull String key, final @NotNull String value) * @param value the context value */ @Override - public void setContexts(final @NotNull String key, final @NotNull Number value) { - final Map map = new HashMap<>(); - map.put("value", value); - setContexts(key, map); + public void setContexts(final @Nullable String key, final @Nullable Number value) { + if (key == null) { + return; + } + if (value == null) { + // unset + setContexts(key, (Object) null); + } else { + final Map map = new HashMap<>(); + map.put("value", value); + setContexts(key, map); + } } /** @@ -708,10 +755,18 @@ public void setContexts(final @NotNull String key, final @NotNull Number value) * @param value the context value */ @Override - public void setContexts(final @NotNull String key, final @NotNull Collection value) { - final Map> map = new HashMap<>(); - map.put("value", value); - setContexts(key, map); + public void setContexts(final @Nullable String key, final @Nullable Collection value) { + if (key == null) { + return; + } + if (value == null) { + // unset + setContexts(key, (Object) null); + } else { + final Map> map = new HashMap<>(); + map.put("value", value); + setContexts(key, map); + } } /** @@ -721,10 +776,18 @@ public void setContexts(final @NotNull String key, final @NotNull Collection * @param value the context value */ @Override - public void setContexts(final @NotNull String key, final @NotNull Object[] value) { - final Map map = new HashMap<>(); - map.put("value", value); - setContexts(key, map); + public void setContexts(final @Nullable String key, final @Nullable Object[] value) { + if (key == null) { + return; + } + if (value == null) { + // unset + setContexts(key, (Object) null); + } else { + final Map map = new HashMap<>(); + map.put("value", value); + setContexts(key, map); + } } /** @@ -734,10 +797,18 @@ public void setContexts(final @NotNull String key, final @NotNull Object[] value * @param value the context value */ @Override - public void setContexts(final @NotNull String key, final @NotNull Character value) { - final Map map = new HashMap<>(); - map.put("value", value); - setContexts(key, map); + public void setContexts(final @Nullable String key, final @Nullable Character value) { + if (key == null) { + return; + } + if (value == null) { + // unset + setContexts(key, (Object) null); + } else { + final Map map = new HashMap<>(); + map.put("value", value); + setContexts(key, map); + } } /** @@ -746,7 +817,10 @@ public void setContexts(final @NotNull String key, final @NotNull Character valu * @param key the Key */ @Override - public void removeContexts(final @NotNull String key) { + public void removeContexts(final @Nullable String key) { + if (key == null) { + return; + } contexts.remove(key); } diff --git a/sentry/src/main/java/io/sentry/Scopes.java b/sentry/src/main/java/io/sentry/Scopes.java index 14025a1d774..92e146b9e46 100644 --- a/sentry/src/main/java/io/sentry/Scopes.java +++ b/sentry/src/main/java/io/sentry/Scopes.java @@ -514,7 +514,7 @@ public void clearBreadcrumbs() { } @Override - public void setTag(final @NotNull String key, final @NotNull String value) { + public void setTag(final @Nullable String key, final @Nullable String value) { if (!isEnabled()) { getOptions() .getLogger() @@ -527,7 +527,7 @@ public void setTag(final @NotNull String key, final @NotNull String value) { } @Override - public void removeTag(final @NotNull String key) { + public void removeTag(final @Nullable String key) { if (!isEnabled()) { getOptions() .getLogger() @@ -540,7 +540,7 @@ public void removeTag(final @NotNull String key) { } @Override - public void setExtra(final @NotNull String key, final @NotNull String value) { + public void setExtra(final @Nullable String key, final @Nullable String value) { if (!isEnabled()) { getOptions() .getLogger() @@ -553,7 +553,7 @@ public void setExtra(final @NotNull String key, final @NotNull String value) { } @Override - public void removeExtra(final @NotNull String key) { + public void removeExtra(final @Nullable String key) { if (!isEnabled()) { getOptions() .getLogger() diff --git a/sentry/src/main/java/io/sentry/ScopesAdapter.java b/sentry/src/main/java/io/sentry/ScopesAdapter.java index 6df6deee3d4..d5d143af52b 100644 --- a/sentry/src/main/java/io/sentry/ScopesAdapter.java +++ b/sentry/src/main/java/io/sentry/ScopesAdapter.java @@ -124,22 +124,22 @@ public void clearBreadcrumbs() { } @Override - public void setTag(@NotNull String key, @NotNull String value) { + public void setTag(@Nullable String key, @Nullable String value) { Sentry.setTag(key, value); } @Override - public void removeTag(@NotNull String key) { + public void removeTag(@Nullable String key) { Sentry.removeTag(key); } @Override - public void setExtra(@NotNull String key, @NotNull String value) { + public void setExtra(@Nullable String key, @Nullable String value) { Sentry.setExtra(key, value); } @Override - public void removeExtra(@NotNull String key) { + public void removeExtra(@Nullable String key) { Sentry.removeExtra(key); } diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index bd5f296b7c2..822609b2779 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -877,7 +877,7 @@ public static void clearBreadcrumbs() { * @param key the key * @param value the value */ - public static void setTag(final @NotNull String key, final @NotNull String value) { + public static void setTag(final @Nullable String key, final @Nullable String value) { getCurrentScopes().setTag(key, value); } @@ -886,7 +886,7 @@ public static void setTag(final @NotNull String key, final @NotNull String value * * @param key the key */ - public static void removeTag(final @NotNull String key) { + public static void removeTag(final @Nullable String key) { getCurrentScopes().removeTag(key); } @@ -897,7 +897,7 @@ public static void removeTag(final @NotNull String key) { * @param key the key * @param value the value */ - public static void setExtra(final @NotNull String key, final @NotNull String value) { + public static void setExtra(final @Nullable String key, final @Nullable String value) { getCurrentScopes().setExtra(key, value); } @@ -906,7 +906,7 @@ public static void setExtra(final @NotNull String key, final @NotNull String val * * @param key the key */ - public static void removeExtra(final @NotNull String key) { + public static void removeExtra(final @Nullable String key) { getCurrentScopes().removeExtra(key); } diff --git a/sentry/src/main/java/io/sentry/SentryBaseEvent.java b/sentry/src/main/java/io/sentry/SentryBaseEvent.java index 58435194a7b..74836c69573 100644 --- a/sentry/src/main/java/io/sentry/SentryBaseEvent.java +++ b/sentry/src/main/java/io/sentry/SentryBaseEvent.java @@ -198,24 +198,31 @@ public void setTags(@Nullable Map tags) { this.tags = CollectionUtils.newHashMap(tags); } - public void removeTag(@NotNull String key) { - if (tags != null) { + public void removeTag(@Nullable String key) { + if (tags != null && key != null) { tags.remove(key); } } - public @Nullable String getTag(final @NotNull String key) { - if (tags != null) { + public @Nullable String getTag(final @Nullable String key) { + if (tags != null && key != null) { return tags.get(key); } return null; } - public void setTag(final @NotNull String key, final @NotNull String value) { + public void setTag(final @Nullable String key, final @Nullable String value) { if (tags == null) { tags = new HashMap<>(); } - tags.put(key, value); + if (key == null) { + return; + } + if (value == null) { + removeTag(key); + } else { + tags.put(key, value); + } } public @Nullable String getRelease() { @@ -298,21 +305,28 @@ public void setExtras(final @Nullable Map extra) { this.extra = CollectionUtils.newHashMap(extra); } - public void setExtra(final @NotNull String key, final @NotNull Object value) { + public void setExtra(final @Nullable String key, final @Nullable Object value) { if (extra == null) { extra = new HashMap<>(); } - extra.put(key, value); + if (key == null) { + return; + } + if (value == null) { + removeExtra(key); + } else { + extra.put(key, value); + } } - public void removeExtra(final @NotNull String key) { - if (extra != null) { + public void removeExtra(final @Nullable String key) { + if (extra != null && key != null) { extra.remove(key); } } - public @Nullable Object getExtra(final @NotNull String key) { - if (extra != null) { + public @Nullable Object getExtra(final @Nullable String key) { + if (extra != null && key != null) { return extra.get(key); } return null; diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index b2a785faf76..b82a25cb2f7 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -1504,8 +1504,15 @@ public void setEnableExternalConfiguration(boolean enableExternalConfiguration) * @param key the key * @param value the value */ - public void setTag(final @NotNull String key, final @NotNull String value) { - this.tags.put(key, value); + public void setTag(final @Nullable String key, final @Nullable String value) { + if (key == null) { + return; + } + if (value == null) { + this.tags.remove(key); + } else { + this.tags.put(key, value); + } } /** diff --git a/sentry/src/main/java/io/sentry/SentryTracer.java b/sentry/src/main/java/io/sentry/SentryTracer.java index 36329db7a71..cc832a136ef 100644 --- a/sentry/src/main/java/io/sentry/SentryTracer.java +++ b/sentry/src/main/java/io/sentry/SentryTracer.java @@ -777,7 +777,7 @@ public void setThrowable(final @Nullable Throwable throwable) { } @Override - public void setTag(final @NotNull String key, final @NotNull String value) { + public void setTag(final @Nullable String key, final @Nullable String value) { if (root.isFinished()) { scopes .getOptions() @@ -790,7 +790,7 @@ public void setTag(final @NotNull String key, final @NotNull String value) { } @Override - public @Nullable String getTag(final @NotNull String key) { + public @Nullable String getTag(final @Nullable String key) { return this.root.getTag(key); } @@ -800,7 +800,7 @@ public boolean isFinished() { } @Override - public void setData(@NotNull String key, @NotNull Object value) { + public void setData(@Nullable String key, @Nullable Object value) { if (root.isFinished()) { scopes .getOptions() @@ -814,7 +814,7 @@ public void setData(@NotNull String key, @NotNull Object value) { } @Override - public @Nullable Object getData(@NotNull String key) { + public @Nullable Object getData(@Nullable String key) { return this.root.getData(key); } @@ -973,7 +973,7 @@ AtomicBoolean isDeadlineTimerRunning() { @ApiStatus.Internal @Override - public void setContext(final @NotNull String key, final @NotNull Object context) { + public void setContext(final @Nullable String key, final @Nullable Object context) { contexts.put(key, context); } diff --git a/sentry/src/main/java/io/sentry/Span.java b/sentry/src/main/java/io/sentry/Span.java index 3f08cca2a58..d3eb2c06551 100644 --- a/sentry/src/main/java/io/sentry/Span.java +++ b/sentry/src/main/java/io/sentry/Span.java @@ -268,12 +268,15 @@ public void setStatus(final @Nullable SpanStatus status) { } @Override - public void setTag(final @NotNull String key, final @NotNull String value) { + public void setTag(final @Nullable String key, final @Nullable String value) { this.context.setTag(key, value); } @Override - public @Nullable String getTag(@NotNull String key) { + public @Nullable String getTag(@Nullable String key) { + if (key == null) { + return null; + } return context.getTags().get(key); } @@ -328,12 +331,22 @@ public Map getTags() { } @Override - public void setData(final @NotNull String key, final @NotNull Object value) { - data.put(key, value); + public void setData(final @Nullable String key, final @Nullable Object value) { + if (key == null) { + return; + } + if (value == null) { + data.remove(key); + } else { + data.put(key, value); + } } @Override - public @Nullable Object getData(final @NotNull String key) { + public @Nullable Object getData(final @Nullable String key) { + if (key == null) { + return null; + } return data.get(key); } @@ -400,7 +413,7 @@ public boolean isNoOp() { } @Override - public void setContext(@NotNull String key, @NotNull Object context) { + public void setContext(@Nullable String key, @Nullable Object context) { this.contexts.put(key, context); } diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index 6f1e4e4eaf9..05f9aa25a5a 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -120,10 +120,15 @@ public void setOperation(final @NotNull String operation) { this.op = Objects.requireNonNull(operation, "operation is required"); } - public void setTag(final @NotNull String name, final @NotNull String value) { - Objects.requireNonNull(name, "name is required"); - Objects.requireNonNull(value, "value is required"); - this.tags.put(name, value); + public void setTag(final @Nullable String name, final @Nullable String value) { + if (name == null) { + return; + } + if (value == null) { + this.tags.remove(name); + } else { + this.tags.put(name, value); + } } public void setDescription(final @Nullable String description) { @@ -238,8 +243,15 @@ public void setInstrumenter(final @NotNull Instrumenter instrumenter) { return data; } - public void setData(final @NotNull String key, final @NotNull Object value) { - data.put(key, value); + public void setData(final @Nullable String key, final @Nullable Object value) { + if (key == null) { + return; + } + if (value == null) { + data.remove(key); + } else { + data.put(key, value); + } } @ApiStatus.Internal diff --git a/sentry/src/main/java/io/sentry/protocol/Contexts.java b/sentry/src/main/java/io/sentry/protocol/Contexts.java index 3705452399c..123436cfae4 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.util.Collections; import java.util.Enumeration; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; @@ -172,23 +173,39 @@ public boolean isEmpty() { return internalStorage.isEmpty(); } - public boolean containsKey(final @NotNull Object key) { + public boolean containsKey(final @Nullable Object key) { + if (key == null) { + return false; + } return internalStorage.containsKey(key); } - public @Nullable Object get(final @NotNull Object key) { + public @Nullable Object get(final @Nullable Object key) { + if (key == null) { + return null; + } return internalStorage.get(key); } - public @Nullable Object put(final @NotNull String key, final @Nullable Object value) { - return internalStorage.put(key, value); + public @Nullable Object put(final @Nullable String key, final @Nullable Object value) { + if (key == null) { + return null; + } + if (value == null) { + return internalStorage.remove(key); + } else { + return internalStorage.put(key, value); + } } - public @Nullable Object set(final @NotNull String key, final @Nullable Object value) { + public @Nullable Object set(final @Nullable String key, final @Nullable Object value) { return put(key, value); } - public @Nullable Object remove(final @NotNull Object key) { + public @Nullable Object remove(final @Nullable Object key) { + if (key == null) { + return null; + } return internalStorage.remove(key); } @@ -200,16 +217,31 @@ public boolean containsKey(final @NotNull Object key) { return internalStorage.entrySet(); } - public void putAll(Map m) { - internalStorage.putAll(m); + public void putAll(final @Nullable Map m) { + if (m == null) { + return; + } + + final @NotNull Map tmpMap = new HashMap<>(); + + for (final @NotNull Map.Entry entry : m.entrySet()) { + if (entry.getKey() != null && entry.getValue() != null) { + tmpMap.put(entry.getKey(), entry.getValue()); + } + } + + internalStorage.putAll(tmpMap); } - public void putAll(final @NotNull Contexts contexts) { + public void putAll(final @Nullable Contexts contexts) { + if (contexts == null) { + return; + } internalStorage.putAll(contexts.internalStorage); } @Override - public boolean equals(Object obj) { + public boolean equals(final @Nullable Object obj) { if (obj != null && obj instanceof Contexts) { final @NotNull Contexts otherContexts = (Contexts) obj; return internalStorage.equals(otherContexts.internalStorage); diff --git a/sentry/src/test/java/io/sentry/BreadcrumbTest.kt b/sentry/src/test/java/io/sentry/BreadcrumbTest.kt index 658e41149bc..2c59869b7cc 100644 --- a/sentry/src/test/java/io/sentry/BreadcrumbTest.kt +++ b/sentry/src/test/java/io/sentry/BreadcrumbTest.kt @@ -7,6 +7,7 @@ import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNotSame import kotlin.test.assertNull +import kotlin.test.assertTrue class BreadcrumbTest { @@ -273,6 +274,44 @@ class BreadcrumbTest { assertNull(breadcrumb.data["name"]) } + @Test + fun `null key data does not throw`() { + val breadcrumb = Breadcrumb() + breadcrumb.setData(null, "v") + assertNull(breadcrumb.getData(null)) + } + + @Test + fun `null key and value data does not throw`() { + val breadcrumb = Breadcrumb() + breadcrumb.setData(null, null) + assertNull(breadcrumb.getData(null)) + assertTrue(breadcrumb.data.isEmpty()) + } + + @Test + fun `null value data does not throw`() { + val breadcrumb = Breadcrumb() + breadcrumb.setData("k", null) + assertNull(breadcrumb.getData("k")) + assertTrue(breadcrumb.data.isEmpty()) + } + + @Test + fun `set null value data removes previous entry`() { + val breadcrumb = Breadcrumb() + breadcrumb.setData("k", "v") + breadcrumb.setData("k", null) + assertNull(breadcrumb.getData("k")) + assertTrue(breadcrumb.data.isEmpty()) + } + + @Test + fun `remove null key data does not throw`() { + val breadcrumb = Breadcrumb() + breadcrumb.removeData(null) + } + class TestKey(val id: Long) { override fun toString(): String { return id.toString() diff --git a/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt b/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt index 2d8d04f22f9..c77ca99cb9d 100644 --- a/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedContextsViewTest.kt @@ -604,4 +604,31 @@ class CombinedContextsViewTest { assertNull(fixture.isolation.get("test")) assertEquals("global", fixture.global.get("test")) } + + @Test + fun `set null value on context does not cause exception`() { + val combined = fixture.getSut() + combined.set("k", null) + assertFalse(combined.containsKey("k")) + } + + @Test + fun `set null key on context does not cause exception`() { + val combined = fixture.getSut() + combined.set(null, "v") + assertFalse(combined.containsKey(null)) + } + + @Test + fun `set null key and value on context does not cause exception`() { + val combined = fixture.getSut() + combined.set(null, null) + assertFalse(combined.containsKey(null)) + } + + @Test + fun `remove null key from context does not cause exception`() { + val combined = fixture.getSut() + combined.remove(null) + } } diff --git a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt index cbb8ed0e9cd..10ae3741cf8 100644 --- a/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt +++ b/sentry/src/test/java/io/sentry/CombinedScopeViewTest.kt @@ -1141,6 +1141,63 @@ class CombinedScopeViewTest { assertEquals(SentryId.EMPTY_ID, fixture.globalScope.replayId) } + @Test + fun `null tags do not cause NPE`() { + val scope = fixture.getSut() + scope.setTag("k", "oldvalue") + scope.setTag(null, null) + scope.setTag("k", null) + scope.setTag(null, "v") + scope.removeTag(null) + kotlin.test.assertTrue(scope.tags.isEmpty()) + } + + @Test + fun `null extras do not cause NPE`() { + val scope = fixture.getSut() + scope.setExtra("k", "oldvalue") + scope.setExtra(null, null) + scope.setExtra("k", null) + scope.setExtra(null, "v") + scope.removeExtra(null) + kotlin.test.assertTrue(scope.extras.isEmpty()) + } + + @Test + fun `null contexts do not cause NPE`() { + val scope = fixture.getSut() + + scope.setContexts("obj", null as Any?) + scope.setContexts("bool", true) + scope.setContexts("string", "hello") + scope.setContexts("num", 100) + scope.setContexts("list", listOf("a", "b")) + scope.setContexts("array", arrayOf("c", "d")) + scope.setContexts("char", 'z') + + kotlin.test.assertFalse(scope.contexts.isEmpty) + + scope.setContexts(null, null as Any?) + scope.setContexts(null, null as Boolean?) + scope.setContexts(null, null as String?) + scope.setContexts(null, null as Number?) + scope.setContexts(null, null as List?) + scope.setContexts(null, null as Array?) + scope.setContexts(null, null as Character?) + + scope.setContexts("obj", null as Any?) + scope.setContexts("bool", null as Boolean?) + scope.setContexts("string", null as String?) + scope.setContexts("num", null as Number?) + scope.setContexts("list", null as List?) + scope.setContexts("array", null as Array?) + scope.setContexts("char", null as Character?) + + scope.removeContexts(null) + + kotlin.test.assertTrue(scope.contexts.isEmpty) + } + private fun createTransaction(name: String, scopes: Scopes? = null): ITransaction { val scopesToUse = scopes ?: fixture.scopes return SentryTracer(TransactionContext(name, "op", TracesSamplingDecision(true)), scopesToUse).also { diff --git a/sentry/src/test/java/io/sentry/ScopeTest.kt b/sentry/src/test/java/io/sentry/ScopeTest.kt index b8025735e8a..6f29ff54b80 100644 --- a/sentry/src/test/java/io/sentry/ScopeTest.kt +++ b/sentry/src/test/java/io/sentry/ScopeTest.kt @@ -1040,6 +1040,63 @@ class ScopeTest { // previously was crashing, see https://github.com/getsentry/sentry-java/issues/3313 } + @Test + fun `null tags do not cause NPE`() { + val scope = Scope(SentryOptions.empty()) + scope.setTag("k", "oldvalue") + scope.setTag(null, null) + scope.setTag("k", null) + scope.setTag(null, "v") + scope.removeTag(null) + assertTrue(scope.tags.isEmpty()) + } + + @Test + fun `null extras do not cause NPE`() { + val scope = Scope(SentryOptions.empty()) + scope.setExtra("k", "oldvalue") + scope.setExtra(null, null) + scope.setExtra("k", null) + scope.setExtra(null, "v") + scope.removeExtra(null) + assertTrue(scope.extras.isEmpty()) + } + + @Test + fun `null contexts do not cause NPE`() { + val scope = Scope(SentryOptions.empty()) + + scope.setContexts("obj", null as Any?) + scope.setContexts("bool", true) + scope.setContexts("string", "hello") + scope.setContexts("num", 100) + scope.setContexts("list", listOf("a", "b")) + scope.setContexts("array", arrayOf("c", "d")) + scope.setContexts("char", 'z') + + assertFalse(scope.contexts.isEmpty) + + scope.setContexts(null, null as Any?) + scope.setContexts(null, null as Boolean?) + scope.setContexts(null, null as String?) + scope.setContexts(null, null as Number?) + scope.setContexts(null, null as List?) + scope.setContexts(null, null as Array?) + scope.setContexts(null, null as Character?) + + scope.setContexts("obj", null as Any?) + scope.setContexts("bool", null as Boolean?) + scope.setContexts("string", null as String?) + scope.setContexts("num", null as Number?) + scope.setContexts("list", null as List?) + scope.setContexts("array", null as Array?) + scope.setContexts("char", null as Character?) + + scope.removeContexts(null) + + assertTrue(scope.contexts.isEmpty) + } + private fun eventProcessor(): EventProcessor { return object : EventProcessor { override fun process(event: SentryEvent, hint: Hint): SentryEvent? { diff --git a/sentry/src/test/java/io/sentry/ScopesTest.kt b/sentry/src/test/java/io/sentry/ScopesTest.kt index fdbbf61b058..23d2dcdd94a 100644 --- a/sentry/src/test/java/io/sentry/ScopesTest.kt +++ b/sentry/src/test/java/io/sentry/ScopesTest.kt @@ -2141,6 +2141,30 @@ class ScopesTest { assertEquals("other.span.origin", transaction.spanContext.origin) } + @Test + fun `null tags do not cause NPE`() { + val scopes = generateScopes() + scopes.setTag(null, null) + scopes.setTag("k", null) + scopes.setTag(null, "v") + scopes.removeTag(null) + assertTrue(scopes.scope.tags.isEmpty()) + assertTrue(scopes.isolationScope.tags.isEmpty()) + assertTrue(scopes.globalScope.tags.isEmpty()) + } + + @Test + fun `null extras do not cause NPE`() { + val scopes = generateScopes() + scopes.setExtra(null, null) + scopes.setExtra("k", null) + scopes.setExtra(null, "v") + scopes.removeExtra(null) + assertTrue(scopes.scope.extras.isEmpty()) + assertTrue(scopes.isolationScope.extras.isEmpty()) + assertTrue(scopes.globalScope.extras.isEmpty()) + } + private val dsnTest = "https://key@sentry.io/proj" private fun generateScopes(optionsConfiguration: Sentry.OptionsConfiguration? = null): IScopes { diff --git a/sentry/src/test/java/io/sentry/SentryEventTest.kt b/sentry/src/test/java/io/sentry/SentryEventTest.kt index 6ad48dfdc0c..9b933884500 100644 --- a/sentry/src/test/java/io/sentry/SentryEventTest.kt +++ b/sentry/src/test/java/io/sentry/SentryEventTest.kt @@ -12,6 +12,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotEquals import kotlin.test.assertNotNull +import kotlin.test.assertNull import kotlin.test.assertTrue class SentryEventTest { @@ -157,4 +158,32 @@ class SentryEventTest { assertEquals(mapOf("key1" to "value1", "key2" to "value2", "key3" to "value3"), it) } } + + @Test + fun `null tag does not cause NPE`() { + val event = SentryEvent() + + event.setTag("k", "oldvalue") + event.setTag(null, null) + event.setTag("k", null) + event.setTag(null, "v") + + assertNull(event.getTag(null)) + assertNull(event.getTag("k")) + assertFalse(event.tags!!.containsKey("k")) + } + + @Test + fun `null extra does not cause NPE`() { + val event = SentryEvent() + + event.setExtra("k", "oldvalue") + event.setExtra(null, null) + event.setExtra("k", null) + event.setExtra(null, "v") + + assertNull(event.getExtra(null)) + assertNull(event.getExtra("k")) + assertFalse(event.extras!!.containsKey("k")) + } } diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index e2f4692357c..c2ae7527d8d 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -695,4 +695,13 @@ class SentryOptionsTest { options.merge(externalOptions) assertEquals(listOf(FilterString("checkin1"), FilterString("checkin2")), options.ignoredCheckIns) } + + @Test + fun `null tag`() { + val options = SentryOptions.empty() + options.setTag("k", "v") + options.setTag("k", null) + options.setTag(null, null) + assertTrue(options.tags.isEmpty()) + } } diff --git a/sentry/src/test/java/io/sentry/SentryTracerTest.kt b/sentry/src/test/java/io/sentry/SentryTracerTest.kt index eb333187b29..8c8bd323c82 100644 --- a/sentry/src/test/java/io/sentry/SentryTracerTest.kt +++ b/sentry/src/test/java/io/sentry/SentryTracerTest.kt @@ -1410,4 +1410,27 @@ class SentryTracerTest { assertNull(transaction.finishDate) transaction.finish() } + + @Test + fun `setting null data does not cause NPE`() { + val transaction = fixture.getSut() + transaction.setData("k", "oldvalue") + transaction.setData(null, null) + transaction.setData("k", null) + transaction.setData(null, "v") + assertNull(transaction.getData(null)) + assertNull(transaction.getData("k")) + assertFalse(transaction.data!!.containsKey("k")) + } + + @Test + fun `setting null tag does not cause NPE`() { + val transaction = fixture.getSut() + transaction.setTag("k", "oldvalue") + transaction.setTag(null, null) + transaction.setTag("k", null) + transaction.setTag(null, "v") + assertNull(transaction.getTag(null)) + assertNull(transaction.getTag("k")) + } } diff --git a/sentry/src/test/java/io/sentry/SpanContextTest.kt b/sentry/src/test/java/io/sentry/SpanContextTest.kt index 0935c10e1f1..bbbb72a0f05 100644 --- a/sentry/src/test/java/io/sentry/SpanContextTest.kt +++ b/sentry/src/test/java/io/sentry/SpanContextTest.kt @@ -3,6 +3,7 @@ package io.sentry import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotNull +import kotlin.test.assertTrue class SpanContextTest { @@ -30,4 +31,22 @@ class SpanContextTest { assertEquals("0.1", trace.baggage?.sampleRate) assertEquals("0.2", trace.baggage?.sampleRand) } + + @Test + fun `null tag`() { + val trace = SpanContext("op") + trace.setTag("k", "v") + trace.setTag("k", null) + trace.setTag(null, null) + assertTrue(trace.tags.isEmpty()) + } + + @Test + fun `null data`() { + val trace = SpanContext("op") + trace.setData("k", "v") + trace.setData("k", null) + trace.setData(null, null) + assertTrue(trace.data.isEmpty()) + } } diff --git a/sentry/src/test/java/io/sentry/SpanTest.kt b/sentry/src/test/java/io/sentry/SpanTest.kt index 79c374413c0..80c72700edf 100644 --- a/sentry/src/test/java/io/sentry/SpanTest.kt +++ b/sentry/src/test/java/io/sentry/SpanTest.kt @@ -547,6 +547,39 @@ class SpanTest { span.finish() } + @Test + fun `null data`() { + val span = fixture.getSut() + span.setData("k", "v") + span.setData("k", null) + span.setData(null, null) + assertNull(span.getData("k")) + assertNull(span.getData(null)) + assertTrue(span.data.isEmpty()) + } + + @Test + fun `null tag`() { + val span = fixture.getSut() + span.setTag("k", "v") + span.setTag("k", null) + span.setTag(null, null) + assertNull(span.getTag("k")) + assertNull(span.getTag(null)) + assertTrue(span.tags.isEmpty()) + } + + @Test + fun `null context`() { + val span = fixture.getSut() + span.setContext("k", "v") + span.setContext("k", null) + span.setContext(null, null) + assertNull(span.contexts.get("k")) + assertNull(span.contexts.get(null)) + assertTrue(span.contexts.isEmpty) + } + private fun getTransaction(transactionContext: TransactionContext = TransactionContext("name", "op")): SentryTracer { return SentryTracer(transactionContext, fixture.scopes) } diff --git a/sentry/src/test/java/io/sentry/protocol/ContextsTest.kt b/sentry/src/test/java/io/sentry/protocol/ContextsTest.kt index 1b422ed9af6..e1ffe73c0cd 100644 --- a/sentry/src/test/java/io/sentry/protocol/ContextsTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/ContextsTest.kt @@ -3,6 +3,7 @@ package io.sentry.protocol import io.sentry.SpanContext import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNotSame @@ -50,4 +51,87 @@ class ContextsTest { assertEquals(contexts["some-property"], clone["some-property"]) assertEquals(contexts.trace!!.description, clone.trace!!.description) } + + @Test + fun `set null value on context does not cause exception`() { + val contexts = Contexts() + contexts.set("k", null) + assertFalse(contexts.containsKey("k")) + } + + @Test + fun `set null key on context does not cause exception`() { + val contexts = Contexts() + contexts.set(null, "v") + assertFalse(contexts.containsKey(null)) + } + + @Test + fun `set null key and value on context does not cause exception`() { + val contexts = Contexts() + contexts.set(null, null) + assertFalse(contexts.containsKey(null)) + } + + @Test + fun `put null value on context does not cause exception`() { + val contexts = Contexts() + contexts.put("k", null) + assertFalse(contexts.containsKey("k")) + } + + @Test + fun `put null value on context removes previous value`() { + val contexts = Contexts() + contexts.put("k", "v") + contexts.put("k", null) + assertFalse(contexts.containsKey("k")) + } + + @Test + fun `put null key on context does not cause exception`() { + val contexts = Contexts() + contexts.put(null, "v") + assertFalse(contexts.containsKey(null)) + } + + @Test + fun `put null key and value on context does not cause exception`() { + val contexts = Contexts() + contexts.put(null, null) + assertFalse(contexts.containsKey(null)) + } + + @Test + fun `remove null key from context does not cause exception`() { + val contexts = Contexts() + contexts.remove(null) + } + + @Test + fun `putAll(null) contexts does not throw`() { + val contexts = Contexts() + val nullContexts: Contexts? = null + contexts.putAll(nullContexts) + } + + @Test + fun `putAll(null) map does not throw`() { + val contexts = Contexts() + val nullMap: Map? = null + contexts.putAll(nullMap) + } + + @Test + fun `putAll map with null key and value does not throw`() { + val contexts = Contexts() + val map = mutableMapOf( + null to null, + "k" to null, + "a" to 1 + ) + contexts.putAll(map) + + assertEquals(listOf("a"), contexts.keys().toList()) + } }