diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index f06d9bc5d7a..2da41627abd 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -195,6 +195,7 @@ object Config { val jsonUnit = "net.javacrumbs.json-unit:json-unit:2.32.0" val hsqldb = "org.hsqldb:hsqldb:2.6.1" val javaFaker = "com.github.javafaker:javafaker:1.0.2" + val msgpack = "org.msgpack:msgpack-core:0.9.8" } object QualityPlugins { 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 8dcfe196c26..0fde2c33dbe 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 @@ -10,6 +10,7 @@ import io.sentry.SentryBaseEvent; import io.sentry.SentryEvent; import io.sentry.SentryLevel; +import io.sentry.SentryReplayEvent; import io.sentry.android.core.internal.util.AndroidMainThreadChecker; import io.sentry.android.core.performance.AppStartMetrics; import io.sentry.android.core.performance.TimeSpan; @@ -256,4 +257,17 @@ private void setSideLoadedInfo(final @NotNull SentryBaseEvent event) { return transaction; } + + @Override + public @NotNull SentryReplayEvent process( + final @NotNull SentryReplayEvent event, final @NotNull Hint hint) { + final boolean applyScopeData = shouldApplyScopeData(event, hint); + if (applyScopeData) { + processNonCachedEvent(event, hint); + } + + setCommons(event, false, applyScopeData); + + return event; + } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt index af32fa3714f..341d15e6081 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt @@ -15,6 +15,7 @@ import io.sentry.Sentry import io.sentry.SentryEnvelope import io.sentry.SentryEvent import io.sentry.SentryOptions +import io.sentry.SentryReplayEvent import io.sentry.Session import io.sentry.TraceContext import io.sentry.UserFeedback @@ -141,6 +142,14 @@ class SessionTrackingIntegrationTest { TODO("Not yet implemented") } + override fun captureReplayEvent( + event: SentryReplayEvent, + scope: IScope?, + hint: Hint? + ): SentryId { + TODO("Not yet implemented") + } + override fun captureUserFeedback(userFeedback: UserFeedback) { TODO("Not yet implemented") } diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 8dc0851098e..b6acae0cdee 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -136,8 +136,8 @@ public final class io/sentry/Breadcrumb : io/sentry/JsonSerializable, io/sentry/ public final class io/sentry/Breadcrumb$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/Breadcrumb; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/Breadcrumb; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/Breadcrumb$JsonKeys { @@ -181,8 +181,8 @@ public final class io/sentry/CheckIn : io/sentry/JsonSerializable, io/sentry/Jso public final class io/sentry/CheckIn$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/CheckIn; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/CheckIn; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/CheckIn$JsonKeys { @@ -226,6 +226,7 @@ public final class io/sentry/DataCategory : java/lang/Enum { public static final field Error Lio/sentry/DataCategory; public static final field Monitor Lio/sentry/DataCategory; public static final field Profile Lio/sentry/DataCategory; + public static final field Replay Lio/sentry/DataCategory; public static final field Security Lio/sentry/DataCategory; public static final field Session Lio/sentry/DataCategory; public static final field Transaction Lio/sentry/DataCategory; @@ -300,6 +301,7 @@ public final class io/sentry/EnvelopeSender : io/sentry/IEnvelopeSender { public abstract interface class io/sentry/EventProcessor { public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; + public fun process (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/SentryReplayEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -387,12 +389,14 @@ public final class io/sentry/Hint { public fun get (Ljava/lang/String;)Ljava/lang/Object; public fun getAs (Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object; public fun getAttachments ()Ljava/util/List; + public fun getReplayRecording ()Lio/sentry/ReplayRecording; public fun getScreenshot ()Lio/sentry/Attachment; public fun getThreadDump ()Lio/sentry/Attachment; public fun getViewHierarchy ()Lio/sentry/Attachment; public fun remove (Ljava/lang/String;)V public fun replaceAttachments (Ljava/util/List;)V public fun set (Ljava/lang/String;Ljava/lang/Object;)V + public fun setReplayRecording (Lio/sentry/ReplayRecording;)V public fun setScreenshot (Lio/sentry/Attachment;)V public fun setThreadDump (Lio/sentry/Attachment;)V public fun setViewHierarchy (Lio/sentry/Attachment;)V @@ -421,6 +425,7 @@ public final class io/sentry/Hub : io/sentry/IHub { public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V @@ -471,6 +476,7 @@ public final class io/sentry/HubAdapter : io/sentry/IHub { public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V @@ -559,6 +565,7 @@ public abstract interface class io/sentry/IHub { public fun captureMessage (Ljava/lang/String;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; public abstract fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public abstract fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; @@ -723,6 +730,7 @@ public abstract interface class io/sentry/ISentryClient { public fun captureException (Ljava/lang/Throwable;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/IScope;)Lio/sentry/protocol/SentryId; + public abstract fun captureReplayEvent (Lio/sentry/SentryReplayEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureSession (Lio/sentry/Session;)V public abstract fun captureSession (Lio/sentry/Session;Lio/sentry/Hint;)V public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;)Lio/sentry/protocol/SentryId; @@ -843,7 +851,7 @@ public final class io/sentry/JavaMemoryCollector : io/sentry/IPerformanceSnapsho } public abstract interface class io/sentry/JsonDeserializer { - public abstract fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public abstract fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/JsonObjectDeserializer { @@ -851,23 +859,38 @@ public final class io/sentry/JsonObjectDeserializer { public fun deserialize (Lio/sentry/JsonObjectReader;)Ljava/lang/Object; } -public final class io/sentry/JsonObjectReader : io/sentry/vendor/gson/stream/JsonReader { +public final class io/sentry/JsonObjectReader : io/sentry/ObjectReader { public fun (Ljava/io/Reader;)V - public static fun dateOrNull (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/util/Date; + public fun beginArray ()V + public fun beginObject ()V + public fun close ()V + public fun endArray ()V + public fun endObject ()V + public fun hasNext ()Z + public fun nextBoolean ()Z public fun nextBooleanOrNull ()Ljava/lang/Boolean; public fun nextDateOrNull (Lio/sentry/ILogger;)Ljava/util/Date; + public fun nextDouble ()D public fun nextDoubleOrNull ()Ljava/lang/Double; - public fun nextFloat ()Ljava/lang/Float; + public fun nextFloat ()F public fun nextFloatOrNull ()Ljava/lang/Float; + public fun nextInt ()I public fun nextIntegerOrNull ()Ljava/lang/Integer; public fun nextListOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/util/List; + public fun nextLong ()J public fun nextLongOrNull ()Ljava/lang/Long; public fun nextMapOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/util/Map; + public fun nextName ()Ljava/lang/String; + public fun nextNull ()V public fun nextObjectOrNull ()Ljava/lang/Object; public fun nextOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/lang/Object; + public fun nextString ()Ljava/lang/String; public fun nextStringOrNull ()Ljava/lang/String; public fun nextTimeZoneOrNull (Lio/sentry/ILogger;)Ljava/util/TimeZone; public fun nextUnknown (Lio/sentry/ILogger;Ljava/util/Map;Ljava/lang/String;)V + public fun peek ()Lio/sentry/vendor/gson/stream/JsonToken; + public fun setLenient (Z)V + public fun skipValue ()V } public final class io/sentry/JsonObjectSerializer { @@ -887,11 +910,13 @@ public final class io/sentry/JsonObjectWriter : io/sentry/ObjectWriter { public synthetic fun endArray ()Lio/sentry/ObjectWriter; public fun endObject ()Lio/sentry/JsonObjectWriter; public synthetic fun endObject ()Lio/sentry/ObjectWriter; + public fun jsonValue (Ljava/lang/String;)Lio/sentry/ObjectWriter; public fun name (Ljava/lang/String;)Lio/sentry/JsonObjectWriter; public synthetic fun name (Ljava/lang/String;)Lio/sentry/ObjectWriter; public fun nullValue ()Lio/sentry/JsonObjectWriter; public synthetic fun nullValue ()Lio/sentry/ObjectWriter; public fun setIndent (Ljava/lang/String;)V + public fun setLenient (Z)V public fun value (D)Lio/sentry/JsonObjectWriter; public synthetic fun value (D)Lio/sentry/ObjectWriter; public fun value (J)Lio/sentry/JsonObjectWriter; @@ -936,6 +961,7 @@ public final class io/sentry/MainEventProcessor : io/sentry/EventProcessor, java public fun (Lio/sentry/SentryOptions;)V public fun close ()V public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; + public fun process (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/SentryReplayEvent; public fun process (Lio/sentry/protocol/SentryTransaction;Lio/sentry/Hint;)Lio/sentry/protocol/SentryTransaction; } @@ -1018,8 +1044,8 @@ public final class io/sentry/MonitorConfig : io/sentry/JsonSerializable, io/sent public final class io/sentry/MonitorConfig$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/MonitorConfig; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/MonitorConfig; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/MonitorConfig$JsonKeys { @@ -1040,8 +1066,8 @@ public final class io/sentry/MonitorContexts : java/util/concurrent/ConcurrentHa public final class io/sentry/MonitorContexts$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/MonitorContexts; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/MonitorContexts; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/MonitorSchedule : io/sentry/JsonSerializable, io/sentry/JsonUnknown { @@ -1063,8 +1089,8 @@ public final class io/sentry/MonitorSchedule : io/sentry/JsonSerializable, io/se public final class io/sentry/MonitorSchedule$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/MonitorSchedule; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/MonitorSchedule; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/MonitorSchedule$JsonKeys { @@ -1119,6 +1145,7 @@ public final class io/sentry/NoOpHub : io/sentry/IHub { public fun captureException (Ljava/lang/Throwable;Lio/sentry/Hint;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;)Lio/sentry/protocol/SentryId; public fun captureMessage (Ljava/lang/String;Lio/sentry/SentryLevel;Lio/sentry/ScopeCallback;)Lio/sentry/protocol/SentryId; + public fun captureReplay (Lio/sentry/SentryReplayEvent;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; public fun captureUserFeedback (Lio/sentry/UserFeedback;)V public fun clearBreadcrumbs ()V @@ -1331,13 +1358,48 @@ public final class io/sentry/NoOpTransportFactory : io/sentry/ITransportFactory public static fun getInstance ()Lio/sentry/NoOpTransportFactory; } +public abstract interface class io/sentry/ObjectReader : java/io/Closeable { + public abstract fun beginArray ()V + public abstract fun beginObject ()V + public static fun dateOrNull (Ljava/lang/String;Lio/sentry/ILogger;)Ljava/util/Date; + public abstract fun endArray ()V + public abstract fun endObject ()V + public abstract fun hasNext ()Z + public abstract fun nextBoolean ()Z + public abstract fun nextBooleanOrNull ()Ljava/lang/Boolean; + public abstract fun nextDateOrNull (Lio/sentry/ILogger;)Ljava/util/Date; + public abstract fun nextDouble ()D + public abstract fun nextDoubleOrNull ()Ljava/lang/Double; + public abstract fun nextFloat ()F + public abstract fun nextFloatOrNull ()Ljava/lang/Float; + public abstract fun nextInt ()I + public abstract fun nextIntegerOrNull ()Ljava/lang/Integer; + public abstract fun nextListOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/util/List; + public abstract fun nextLong ()J + public abstract fun nextLongOrNull ()Ljava/lang/Long; + public abstract fun nextMapOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/util/Map; + public abstract fun nextName ()Ljava/lang/String; + public abstract fun nextNull ()V + public abstract fun nextObjectOrNull ()Ljava/lang/Object; + public abstract fun nextOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/lang/Object; + public abstract fun nextString ()Ljava/lang/String; + public abstract fun nextStringOrNull ()Ljava/lang/String; + public abstract fun nextTimeZoneOrNull (Lio/sentry/ILogger;)Ljava/util/TimeZone; + public abstract fun nextUnknown (Lio/sentry/ILogger;Ljava/util/Map;Ljava/lang/String;)V + public abstract fun peek ()Lio/sentry/vendor/gson/stream/JsonToken; + public abstract fun setLenient (Z)V + public abstract fun skipValue ()V +} + public abstract interface class io/sentry/ObjectWriter { public abstract fun beginArray ()Lio/sentry/ObjectWriter; public abstract fun beginObject ()Lio/sentry/ObjectWriter; public abstract fun endArray ()Lio/sentry/ObjectWriter; public abstract fun endObject ()Lio/sentry/ObjectWriter; + public abstract fun jsonValue (Ljava/lang/String;)Lio/sentry/ObjectWriter; public abstract fun name (Ljava/lang/String;)Lio/sentry/ObjectWriter; public abstract fun nullValue ()Lio/sentry/ObjectWriter; + public abstract fun setLenient (Z)V public abstract fun value (D)Lio/sentry/ObjectWriter; public abstract fun value (J)Lio/sentry/ObjectWriter; public abstract fun value (Lio/sentry/ILogger;Ljava/lang/Object;)Lio/sentry/ObjectWriter; @@ -1426,8 +1488,8 @@ public final class io/sentry/ProfilingTraceData : io/sentry/JsonSerializable, io public final class io/sentry/ProfilingTraceData$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/ProfilingTraceData; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/ProfilingTraceData; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/ProfilingTraceData$JsonKeys { @@ -1484,8 +1546,8 @@ public final class io/sentry/ProfilingTransactionData : io/sentry/JsonSerializab public final class io/sentry/ProfilingTransactionData$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/ProfilingTransactionData; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/ProfilingTransactionData; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/ProfilingTransactionData$JsonKeys { @@ -1519,6 +1581,30 @@ public final class io/sentry/PropagationContext { public fun traceContext ()Lio/sentry/TraceContext; } +public final class io/sentry/ReplayRecording : io/sentry/JsonSerializable, io/sentry/JsonUnknown { + public fun ()V + public fun equals (Ljava/lang/Object;)Z + public fun getPayload ()Ljava/util/List; + public fun getSegmentId ()Ljava/lang/Integer; + public fun getUnknown ()Ljava/util/Map; + public fun hashCode ()I + public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public fun setPayload (Ljava/util/List;)V + public fun setSegmentId (Ljava/lang/Integer;)V + public fun setUnknown (Ljava/util/Map;)V +} + +public final class io/sentry/ReplayRecording$Deserializer : io/sentry/JsonDeserializer { + public fun ()V + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/ReplayRecording; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; +} + +public final class io/sentry/ReplayRecording$JsonKeys { + public static final field SEGMENT_ID Ljava/lang/String; + public fun ()V +} + public final class io/sentry/RequestDetails { public fun (Ljava/lang/String;Ljava/util/Map;)V public fun getHeaders ()Ljava/util/Map; @@ -1742,8 +1828,8 @@ public final class io/sentry/SentryAppStartProfilingOptions : io/sentry/JsonSeri public final class io/sentry/SentryAppStartProfilingOptions$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryAppStartProfilingOptions; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryAppStartProfilingOptions; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/SentryAppStartProfilingOptions$JsonKeys { @@ -1809,7 +1895,7 @@ public abstract class io/sentry/SentryBaseEvent { public final class io/sentry/SentryBaseEvent$Deserializer { public fun ()V - public fun deserializeValue (Lio/sentry/SentryBaseEvent;Ljava/lang/String;Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Z + public fun deserializeValue (Lio/sentry/SentryBaseEvent;Ljava/lang/String;Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Z } public final class io/sentry/SentryBaseEvent$JsonKeys { @@ -1839,6 +1925,7 @@ public final class io/sentry/SentryClient : io/sentry/ISentryClient { public fun captureCheckIn (Lio/sentry/CheckIn;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureEnvelope (Lio/sentry/SentryEnvelope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureEvent (Lio/sentry/SentryEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; + public fun captureReplayEvent (Lio/sentry/SentryReplayEvent;Lio/sentry/IScope;Lio/sentry/Hint;)Lio/sentry/protocol/SentryId; public fun captureSession (Lio/sentry/Session;Lio/sentry/Hint;)V public fun captureTransaction (Lio/sentry/protocol/SentryTransaction;Lio/sentry/TraceContext;Lio/sentry/IScope;Lio/sentry/Hint;Lio/sentry/ProfilingTraceData;)Lio/sentry/protocol/SentryId; public fun captureUserFeedback (Lio/sentry/UserFeedback;)V @@ -1899,8 +1986,8 @@ public final class io/sentry/SentryEnvelopeHeader : io/sentry/JsonSerializable, public final class io/sentry/SentryEnvelopeHeader$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryEnvelopeHeader; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryEnvelopeHeader; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/SentryEnvelopeHeader$JsonKeys { @@ -1917,6 +2004,7 @@ public final class io/sentry/SentryEnvelopeItem { public static fun fromClientReport (Lio/sentry/ISerializer;Lio/sentry/clientreport/ClientReport;)Lio/sentry/SentryEnvelopeItem; public static fun fromEvent (Lio/sentry/ISerializer;Lio/sentry/SentryBaseEvent;)Lio/sentry/SentryEnvelopeItem; public static fun fromProfilingTrace (Lio/sentry/ProfilingTraceData;JLio/sentry/ISerializer;)Lio/sentry/SentryEnvelopeItem; + public static fun fromReplay (Lio/sentry/ISerializer;Lio/sentry/ILogger;Lio/sentry/SentryReplayEvent;Lio/sentry/ReplayRecording;)Lio/sentry/SentryEnvelopeItem; public static fun fromSession (Lio/sentry/ISerializer;Lio/sentry/Session;)Lio/sentry/SentryEnvelopeItem; public static fun fromUserFeedback (Lio/sentry/ISerializer;Lio/sentry/UserFeedback;)Lio/sentry/SentryEnvelopeItem; public fun getClientReport (Lio/sentry/ISerializer;)Lio/sentry/clientreport/ClientReport; @@ -1940,8 +2028,8 @@ public final class io/sentry/SentryEnvelopeItemHeader : io/sentry/JsonSerializab public final class io/sentry/SentryEnvelopeItemHeader$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryEnvelopeItemHeader; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryEnvelopeItemHeader; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/SentryEnvelopeItemHeader$JsonKeys { @@ -1987,8 +2075,8 @@ public final class io/sentry/SentryEvent : io/sentry/SentryBaseEvent, io/sentry/ public final class io/sentry/SentryEvent$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryEvent; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryEvent; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/SentryEvent$JsonKeys { @@ -2047,6 +2135,7 @@ public final class io/sentry/SentryItemType : java/lang/Enum, io/sentry/JsonSeri public static final field Profile Lio/sentry/SentryItemType; public static final field ReplayEvent Lio/sentry/SentryItemType; public static final field ReplayRecording Lio/sentry/SentryItemType; + public static final field ReplayVideo Lio/sentry/SentryItemType; public static final field Session Lio/sentry/SentryItemType; public static final field Transaction Lio/sentry/SentryItemType; public static final field Unknown Lio/sentry/SentryItemType; @@ -2097,8 +2186,8 @@ public final class io/sentry/SentryLockReason : io/sentry/JsonSerializable, io/s public final class io/sentry/SentryLockReason$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryLockReason; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryLockReason; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/SentryLockReason$JsonKeys { @@ -2374,6 +2463,70 @@ public abstract interface class io/sentry/SentryOptions$TracesSamplerCallback { public abstract fun sample (Lio/sentry/SamplingContext;)Ljava/lang/Double; } +public final class io/sentry/SentryReplayEvent : io/sentry/SentryBaseEvent, io/sentry/JsonSerializable, io/sentry/JsonUnknown { + public static final field REPLAY_EVENT_TYPE Ljava/lang/String; + public static final field REPLAY_VIDEO_MAX_SIZE J + public fun ()V + public fun equals (Ljava/lang/Object;)Z + public fun getErrorIds ()Ljava/util/List; + public fun getReplayId ()Lio/sentry/protocol/SentryId; + public fun getReplayStartTimestamp ()Ljava/util/Date; + public fun getReplayType ()Lio/sentry/SentryReplayEvent$ReplayType; + public fun getSegmentId ()I + public fun getTimestamp ()Ljava/util/Date; + public fun getTraceIds ()Ljava/util/List; + public fun getType ()Ljava/lang/String; + public fun getUnknown ()Ljava/util/Map; + public fun getUrls ()Ljava/util/List; + public fun getVideoFile ()Ljava/io/File; + public fun hashCode ()I + public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public fun setErrorIds (Ljava/util/List;)V + public fun setReplayId (Lio/sentry/protocol/SentryId;)V + public fun setReplayStartTimestamp (Ljava/util/Date;)V + public fun setReplayType (Lio/sentry/SentryReplayEvent$ReplayType;)V + public fun setSegmentId (I)V + public fun setTimestamp (Ljava/util/Date;)V + public fun setTraceIds (Ljava/util/List;)V + public fun setType (Ljava/lang/String;)V + public fun setUnknown (Ljava/util/Map;)V + public fun setUrls (Ljava/util/List;)V + public fun setVideoFile (Ljava/io/File;)V +} + +public final class io/sentry/SentryReplayEvent$Deserializer : io/sentry/JsonDeserializer { + public fun ()V + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryReplayEvent; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; +} + +public final class io/sentry/SentryReplayEvent$JsonKeys { + public static final field ERROR_IDS Ljava/lang/String; + public static final field REPLAY_ID Ljava/lang/String; + public static final field REPLAY_START_TIMESTAMP Ljava/lang/String; + public static final field REPLAY_TYPE Ljava/lang/String; + public static final field SEGMENT_ID Ljava/lang/String; + public static final field TIMESTAMP Ljava/lang/String; + public static final field TRACE_IDS Ljava/lang/String; + public static final field TYPE Ljava/lang/String; + public static final field URLS Ljava/lang/String; + public fun ()V +} + +public final class io/sentry/SentryReplayEvent$ReplayType : java/lang/Enum, io/sentry/JsonSerializable { + public static final field BUFFER Lio/sentry/SentryReplayEvent$ReplayType; + public static final field SESSION Lio/sentry/SentryReplayEvent$ReplayType; + public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public static fun valueOf (Ljava/lang/String;)Lio/sentry/SentryReplayEvent$ReplayType; + public static fun values ()[Lio/sentry/SentryReplayEvent$ReplayType; +} + +public final class io/sentry/SentryReplayEvent$ReplayType$Deserializer : io/sentry/JsonDeserializer { + public fun ()V + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryReplayEvent$ReplayType; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; +} + public final class io/sentry/SentrySpanStorage { public fun get (Ljava/lang/String;)Lio/sentry/ISpan; public static fun getInstance ()Lio/sentry/SentrySpanStorage; @@ -2495,8 +2648,8 @@ public final class io/sentry/Session : io/sentry/JsonSerializable, io/sentry/Jso public final class io/sentry/Session$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/Session; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/Session; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/Session$JsonKeys { @@ -2617,8 +2770,8 @@ public class io/sentry/SpanContext : io/sentry/JsonSerializable, io/sentry/JsonU public final class io/sentry/SpanContext$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SpanContext; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SpanContext; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/SpanContext$JsonKeys { @@ -2662,8 +2815,8 @@ public final class io/sentry/SpanId : io/sentry/JsonSerializable { public final class io/sentry/SpanId$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SpanId; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SpanId; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public class io/sentry/SpanOptions { @@ -2704,8 +2857,8 @@ public final class io/sentry/SpanStatus : java/lang/Enum, io/sentry/JsonSerializ public final class io/sentry/SpanStatus$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SpanStatus; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/SpanStatus; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/SystemOutLogger : io/sentry/ILogger { @@ -2733,8 +2886,8 @@ public final class io/sentry/TraceContext : io/sentry/JsonSerializable, io/sentr public final class io/sentry/TraceContext$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/TraceContext; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/TraceContext; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/TraceContext$JsonKeys { @@ -2893,8 +3046,8 @@ public final class io/sentry/UserFeedback : io/sentry/JsonSerializable, io/sentr public final class io/sentry/UserFeedback$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/UserFeedback; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/UserFeedback; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/UserFeedback$JsonKeys { @@ -3005,8 +3158,8 @@ public final class io/sentry/clientreport/ClientReport : io/sentry/JsonSerializa public final class io/sentry/clientreport/ClientReport$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/clientreport/ClientReport; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/clientreport/ClientReport; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/clientreport/ClientReport$JsonKeys { @@ -3050,8 +3203,8 @@ public final class io/sentry/clientreport/DiscardedEvent : io/sentry/JsonSeriali public final class io/sentry/clientreport/DiscardedEvent$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/clientreport/DiscardedEvent; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/clientreport/DiscardedEvent; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/clientreport/DiscardedEvent$JsonKeys { @@ -3344,8 +3497,8 @@ public final class io/sentry/profilemeasurements/ProfileMeasurement : io/sentry/ public final class io/sentry/profilemeasurements/ProfileMeasurement$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/profilemeasurements/ProfileMeasurement; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/profilemeasurements/ProfileMeasurement; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/profilemeasurements/ProfileMeasurement$JsonKeys { @@ -3368,8 +3521,8 @@ public final class io/sentry/profilemeasurements/ProfileMeasurementValue : io/se public final class io/sentry/profilemeasurements/ProfileMeasurementValue$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/profilemeasurements/ProfileMeasurementValue; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/profilemeasurements/ProfileMeasurementValue; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/profilemeasurements/ProfileMeasurementValue$JsonKeys { @@ -3410,8 +3563,8 @@ public final class io/sentry/protocol/App : io/sentry/JsonSerializable, io/sentr public final class io/sentry/protocol/App$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/App; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/App; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/App$JsonKeys { @@ -3444,8 +3597,8 @@ public final class io/sentry/protocol/Browser : io/sentry/JsonSerializable, io/s public final class io/sentry/protocol/Browser$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Browser; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Browser; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Browser$JsonKeys { @@ -3479,8 +3632,8 @@ public final class io/sentry/protocol/Contexts : java/util/concurrent/Concurrent public final class io/sentry/protocol/Contexts$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Contexts; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Contexts; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/DebugImage : io/sentry/JsonSerializable, io/sentry/JsonUnknown { @@ -3513,8 +3666,8 @@ public final class io/sentry/protocol/DebugImage : io/sentry/JsonSerializable, i public final class io/sentry/protocol/DebugImage$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/DebugImage; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/DebugImage; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/DebugImage$JsonKeys { @@ -3543,8 +3696,8 @@ public final class io/sentry/protocol/DebugMeta : io/sentry/JsonSerializable, io public final class io/sentry/protocol/DebugMeta$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/DebugMeta; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/DebugMeta; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/DebugMeta$JsonKeys { @@ -3633,8 +3786,8 @@ public final class io/sentry/protocol/Device : io/sentry/JsonSerializable, io/se public final class io/sentry/protocol/Device$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Device; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Device; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Device$DeviceOrientation : java/lang/Enum, io/sentry/JsonSerializable { @@ -3647,8 +3800,8 @@ public final class io/sentry/protocol/Device$DeviceOrientation : java/lang/Enum, public final class io/sentry/protocol/Device$DeviceOrientation$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Device$DeviceOrientation; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Device$DeviceOrientation; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Device$JsonKeys { @@ -3706,8 +3859,8 @@ public final class io/sentry/protocol/Geo : io/sentry/JsonSerializable, io/sentr public final class io/sentry/protocol/Geo$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Geo; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Geo; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Geo$JsonKeys { @@ -3747,8 +3900,8 @@ public final class io/sentry/protocol/Gpu : io/sentry/JsonSerializable, io/sentr public final class io/sentry/protocol/Gpu$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Gpu; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Gpu; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Gpu$JsonKeys { @@ -3783,8 +3936,8 @@ public final class io/sentry/protocol/MeasurementValue : io/sentry/JsonSerializa public final class io/sentry/protocol/MeasurementValue$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/MeasurementValue; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/MeasurementValue; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/MeasurementValue$JsonKeys { @@ -3817,8 +3970,8 @@ public final class io/sentry/protocol/Mechanism : io/sentry/JsonSerializable, io public final class io/sentry/protocol/Mechanism$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Mechanism; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Mechanism; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Mechanism$JsonKeys { @@ -3847,8 +4000,8 @@ public final class io/sentry/protocol/Message : io/sentry/JsonSerializable, io/s public final class io/sentry/protocol/Message$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Message; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Message; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Message$JsonKeys { @@ -3882,8 +4035,8 @@ public final class io/sentry/protocol/OperatingSystem : io/sentry/JsonSerializab public final class io/sentry/protocol/OperatingSystem$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/OperatingSystem; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/OperatingSystem; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/OperatingSystem$JsonKeys { @@ -3930,8 +4083,8 @@ public final class io/sentry/protocol/Request : io/sentry/JsonSerializable, io/s public final class io/sentry/protocol/Request$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Request; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Request; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Request$JsonKeys { @@ -3970,8 +4123,8 @@ public final class io/sentry/protocol/Response : io/sentry/JsonSerializable, io/ public final class io/sentry/protocol/Response$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Response; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/Response; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/Response$JsonKeys { @@ -4000,8 +4153,8 @@ public final class io/sentry/protocol/SdkInfo : io/sentry/JsonSerializable, io/s public final class io/sentry/protocol/SdkInfo$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SdkInfo; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SdkInfo; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SdkInfo$JsonKeys { @@ -4034,8 +4187,8 @@ public final class io/sentry/protocol/SdkVersion : io/sentry/JsonSerializable, i public final class io/sentry/protocol/SdkVersion$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SdkVersion; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SdkVersion; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SdkVersion$JsonKeys { @@ -4067,8 +4220,8 @@ public final class io/sentry/protocol/SentryException : io/sentry/JsonSerializab public final class io/sentry/protocol/SentryException$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryException; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryException; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentryException$JsonKeys { @@ -4094,8 +4247,8 @@ public final class io/sentry/protocol/SentryId : io/sentry/JsonSerializable { public final class io/sentry/protocol/SentryId$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryId; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryId; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentryPackage : io/sentry/JsonSerializable, io/sentry/JsonUnknown { @@ -4113,8 +4266,8 @@ public final class io/sentry/protocol/SentryPackage : io/sentry/JsonSerializable public final class io/sentry/protocol/SentryPackage$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryPackage; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryPackage; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentryPackage$JsonKeys { @@ -4139,8 +4292,8 @@ public final class io/sentry/protocol/SentryRuntime : io/sentry/JsonSerializable public final class io/sentry/protocol/SentryRuntime$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryRuntime; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryRuntime; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentryRuntime$JsonKeys { @@ -4173,8 +4326,8 @@ public final class io/sentry/protocol/SentrySpan : io/sentry/JsonSerializable, i public final class io/sentry/protocol/SentrySpan$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentrySpan; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentrySpan; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentrySpan$JsonKeys { @@ -4243,8 +4396,8 @@ public final class io/sentry/protocol/SentryStackFrame : io/sentry/JsonSerializa public final class io/sentry/protocol/SentryStackFrame$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryStackFrame; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryStackFrame; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentryStackFrame$JsonKeys { @@ -4284,8 +4437,8 @@ public final class io/sentry/protocol/SentryStackTrace : io/sentry/JsonSerializa public final class io/sentry/protocol/SentryStackTrace$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryStackTrace; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryStackTrace; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentryStackTrace$JsonKeys { @@ -4324,8 +4477,8 @@ public final class io/sentry/protocol/SentryThread : io/sentry/JsonSerializable, public final class io/sentry/protocol/SentryThread$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryThread; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryThread; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentryThread$JsonKeys { @@ -4362,8 +4515,8 @@ public final class io/sentry/protocol/SentryTransaction : io/sentry/SentryBaseEv public final class io/sentry/protocol/SentryTransaction$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryTransaction; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/SentryTransaction; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/SentryTransaction$JsonKeys { @@ -4386,8 +4539,8 @@ public final class io/sentry/protocol/TransactionInfo : io/sentry/JsonSerializab public final class io/sentry/protocol/TransactionInfo$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/TransactionInfo; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/TransactionInfo; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/TransactionInfo$JsonKeys { @@ -4438,8 +4591,8 @@ public final class io/sentry/protocol/User : io/sentry/JsonSerializable, io/sent public final class io/sentry/protocol/User$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/User; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/User; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/User$JsonKeys { @@ -4466,8 +4619,8 @@ public final class io/sentry/protocol/ViewHierarchy : io/sentry/JsonSerializable public final class io/sentry/protocol/ViewHierarchy$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/ViewHierarchy; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/ViewHierarchy; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/ViewHierarchy$JsonKeys { @@ -4507,8 +4660,8 @@ public final class io/sentry/protocol/ViewHierarchyNode : io/sentry/JsonSerializ public final class io/sentry/protocol/ViewHierarchyNode$Deserializer : io/sentry/JsonDeserializer { public fun ()V - public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/ViewHierarchyNode; - public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/protocol/ViewHierarchyNode; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; } public final class io/sentry/protocol/ViewHierarchyNode$JsonKeys { @@ -4526,6 +4679,152 @@ public final class io/sentry/protocol/ViewHierarchyNode$JsonKeys { public fun ()V } +public abstract class io/sentry/rrweb/RRWebEvent { + protected fun ()V + protected fun (Lio/sentry/rrweb/RRWebEventType;)V + public fun equals (Ljava/lang/Object;)Z + public fun getTimestamp ()J + public fun getType ()Lio/sentry/rrweb/RRWebEventType; + public fun hashCode ()I + public fun setTimestamp (J)V + public fun setType (Lio/sentry/rrweb/RRWebEventType;)V +} + +public final class io/sentry/rrweb/RRWebEvent$Deserializer { + public fun ()V + public fun deserializeValue (Lio/sentry/rrweb/RRWebEvent;Ljava/lang/String;Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Z +} + +public final class io/sentry/rrweb/RRWebEvent$JsonKeys { + public static final field TAG Ljava/lang/String; + public static final field TIMESTAMP Ljava/lang/String; + public static final field TYPE Ljava/lang/String; + public fun ()V +} + +public final class io/sentry/rrweb/RRWebEvent$Serializer { + public fun ()V + public fun serialize (Lio/sentry/rrweb/RRWebEvent;Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V +} + +public final class io/sentry/rrweb/RRWebEventType : java/lang/Enum, io/sentry/JsonSerializable { + public static final field Custom Lio/sentry/rrweb/RRWebEventType; + public static final field DomContentLoaded Lio/sentry/rrweb/RRWebEventType; + public static final field FullSnapshot Lio/sentry/rrweb/RRWebEventType; + public static final field IncrementalSnapshot Lio/sentry/rrweb/RRWebEventType; + public static final field Load Lio/sentry/rrweb/RRWebEventType; + public static final field Meta Lio/sentry/rrweb/RRWebEventType; + public static final field Plugin Lio/sentry/rrweb/RRWebEventType; + public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public static fun valueOf (Ljava/lang/String;)Lio/sentry/rrweb/RRWebEventType; + public static fun values ()[Lio/sentry/rrweb/RRWebEventType; +} + +public final class io/sentry/rrweb/RRWebEventType$Deserializer : io/sentry/JsonDeserializer { + public fun ()V + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/rrweb/RRWebEventType; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; +} + +public final class io/sentry/rrweb/RRWebMetaEvent : io/sentry/rrweb/RRWebEvent, io/sentry/JsonSerializable, io/sentry/JsonUnknown { + public fun ()V + public fun equals (Ljava/lang/Object;)Z + public fun getDataUnknown ()Ljava/util/Map; + public fun getHeight ()I + public fun getHref ()Ljava/lang/String; + public fun getUnknown ()Ljava/util/Map; + public fun getWidth ()I + public fun hashCode ()I + public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public fun setDataUnknown (Ljava/util/Map;)V + public fun setHeight (I)V + public fun setHref (Ljava/lang/String;)V + public fun setUnknown (Ljava/util/Map;)V + public fun setWidth (I)V +} + +public final class io/sentry/rrweb/RRWebMetaEvent$Deserializer : io/sentry/JsonDeserializer { + public fun ()V + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/rrweb/RRWebMetaEvent; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; +} + +public final class io/sentry/rrweb/RRWebMetaEvent$JsonKeys { + public static final field DATA Ljava/lang/String; + public static final field HEIGHT Ljava/lang/String; + public static final field HREF Ljava/lang/String; + public static final field WIDTH Ljava/lang/String; + public fun ()V +} + +public final class io/sentry/rrweb/RRWebVideoEvent : io/sentry/rrweb/RRWebEvent, io/sentry/JsonSerializable, io/sentry/JsonUnknown { + public static final field EVENT_TAG Ljava/lang/String; + public static final field REPLAY_CONTAINER Ljava/lang/String; + public static final field REPLAY_ENCODING Ljava/lang/String; + public static final field REPLAY_FRAME_RATE_TYPE_CONSTANT Ljava/lang/String; + public static final field REPLAY_FRAME_RATE_TYPE_VARIABLE Ljava/lang/String; + public fun ()V + public fun equals (Ljava/lang/Object;)Z + public fun getContainer ()Ljava/lang/String; + public fun getDataUnknown ()Ljava/util/Map; + public fun getDuration ()I + public fun getEncoding ()Ljava/lang/String; + public fun getFrameCount ()I + public fun getFrameRate ()I + public fun getFrameRateType ()Ljava/lang/String; + public fun getHeight ()I + public fun getLeft ()I + public fun getPayloadUnknown ()Ljava/util/Map; + public fun getSegmentId ()I + public fun getSize ()J + public fun getTag ()Ljava/lang/String; + public fun getTop ()I + public fun getUnknown ()Ljava/util/Map; + public fun getWidth ()I + public fun hashCode ()I + public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V + public fun setContainer (Ljava/lang/String;)V + public fun setDataUnknown (Ljava/util/Map;)V + public fun setDuration (I)V + public fun setEncoding (Ljava/lang/String;)V + public fun setFrameCount (I)V + public fun setFrameRate (I)V + public fun setFrameRateType (Ljava/lang/String;)V + public fun setHeight (I)V + public fun setLeft (I)V + public fun setPayloadUnknown (Ljava/util/Map;)V + public fun setSegmentId (I)V + public fun setSize (J)V + public fun setTag (Ljava/lang/String;)V + public fun setTop (I)V + public fun setUnknown (Ljava/util/Map;)V + public fun setWidth (I)V +} + +public final class io/sentry/rrweb/RRWebVideoEvent$Deserializer : io/sentry/JsonDeserializer { + public fun ()V + public fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Lio/sentry/rrweb/RRWebVideoEvent; + public synthetic fun deserialize (Lio/sentry/ObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; +} + +public final class io/sentry/rrweb/RRWebVideoEvent$JsonKeys { + public static final field CONTAINER Ljava/lang/String; + public static final field DATA Ljava/lang/String; + public static final field DURATION Ljava/lang/String; + public static final field ENCODING Ljava/lang/String; + public static final field FRAME_COUNT Ljava/lang/String; + public static final field FRAME_RATE Ljava/lang/String; + public static final field FRAME_RATE_TYPE Ljava/lang/String; + public static final field HEIGHT Ljava/lang/String; + public static final field LEFT Ljava/lang/String; + public static final field PAYLOAD Ljava/lang/String; + public static final field SEGMENT_ID Ljava/lang/String; + public static final field SIZE Ljava/lang/String; + public static final field TOP Ljava/lang/String; + public static final field WIDTH Ljava/lang/String; + public fun ()V +} + public final class io/sentry/transport/AsyncHttpTransport : io/sentry/transport/ITransport { public fun (Lio/sentry/SentryOptions;Lio/sentry/transport/RateLimiter;Lio/sentry/transport/ITransportGate;Lio/sentry/RequestDetails;)V public fun (Lio/sentry/transport/QueuedThreadPoolExecutor;Lio/sentry/SentryOptions;Lio/sentry/transport/RateLimiter;Lio/sentry/transport/ITransportGate;Lio/sentry/transport/HttpConnection;)V @@ -4727,6 +5026,40 @@ public final class io/sentry/util/LogUtils { public static fun logNotInstanceOf (Ljava/lang/Class;Ljava/lang/Object;Lio/sentry/ILogger;)V } +public final class io/sentry/util/MapObjectReader : io/sentry/ObjectReader { + public fun (Ljava/util/Map;)V + public fun beginArray ()V + public fun beginObject ()V + public fun close ()V + public fun endArray ()V + public fun endObject ()V + public fun hasNext ()Z + public fun nextBoolean ()Z + public fun nextBooleanOrNull ()Ljava/lang/Boolean; + public fun nextDateOrNull (Lio/sentry/ILogger;)Ljava/util/Date; + public fun nextDouble ()D + public fun nextDoubleOrNull ()Ljava/lang/Double; + public fun nextFloat ()F + public fun nextFloatOrNull ()Ljava/lang/Float; + public fun nextInt ()I + public fun nextIntegerOrNull ()Ljava/lang/Integer; + public fun nextListOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/util/List; + public fun nextLong ()J + public fun nextLongOrNull ()Ljava/lang/Long; + public fun nextMapOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/util/Map; + public fun nextName ()Ljava/lang/String; + public fun nextNull ()V + public fun nextObjectOrNull ()Ljava/lang/Object; + public fun nextOrNull (Lio/sentry/ILogger;Lio/sentry/JsonDeserializer;)Ljava/lang/Object; + public fun nextString ()Ljava/lang/String; + public fun nextStringOrNull ()Ljava/lang/String; + public fun nextTimeZoneOrNull (Lio/sentry/ILogger;)Ljava/util/TimeZone; + public fun nextUnknown (Lio/sentry/ILogger;Ljava/util/Map;Ljava/lang/String;)V + public fun peek ()Lio/sentry/vendor/gson/stream/JsonToken; + public fun setLenient (Z)V + public fun skipValue ()V +} + public final class io/sentry/util/MapObjectWriter : io/sentry/ObjectWriter { public fun (Ljava/util/Map;)V public synthetic fun beginArray ()Lio/sentry/ObjectWriter; @@ -4737,10 +5070,12 @@ public final class io/sentry/util/MapObjectWriter : io/sentry/ObjectWriter { public fun endArray ()Lio/sentry/util/MapObjectWriter; public synthetic fun endObject ()Lio/sentry/ObjectWriter; public fun endObject ()Lio/sentry/util/MapObjectWriter; + public fun jsonValue (Ljava/lang/String;)Lio/sentry/ObjectWriter; public synthetic fun name (Ljava/lang/String;)Lio/sentry/ObjectWriter; public fun name (Ljava/lang/String;)Lio/sentry/util/MapObjectWriter; public synthetic fun nullValue ()Lio/sentry/ObjectWriter; public fun nullValue ()Lio/sentry/util/MapObjectWriter; + public fun setLenient (Z)V public synthetic fun value (D)Lio/sentry/ObjectWriter; public fun value (D)Lio/sentry/util/MapObjectWriter; public synthetic fun value (J)Lio/sentry/ObjectWriter; diff --git a/sentry/build.gradle.kts b/sentry/build.gradle.kts index 2f35cbd4f72..08efc550d5a 100644 --- a/sentry/build.gradle.kts +++ b/sentry/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { testImplementation(Config.TestLibs.mockitoInline) testImplementation(Config.TestLibs.awaitility) testImplementation(Config.TestLibs.javaFaker) + testImplementation(Config.TestLibs.msgpack) testImplementation(projects.sentryTestSupport) } diff --git a/sentry/src/main/java/io/sentry/Breadcrumb.java b/sentry/src/main/java/io/sentry/Breadcrumb.java index fe2055c336c..da1453bc68b 100644 --- a/sentry/src/main/java/io/sentry/Breadcrumb.java +++ b/sentry/src/main/java/io/sentry/Breadcrumb.java @@ -86,8 +86,7 @@ public static Breadcrumb fromMap( switch (entry.getKey()) { case JsonKeys.TIMESTAMP: if (value instanceof String) { - Date deserializedDate = - JsonObjectReader.dateOrNull((String) value, options.getLogger()); + Date deserializedDate = ObjectReader.dateOrNull((String) value, options.getLogger()); if (deserializedDate != null) { timestamp = deserializedDate; } @@ -700,8 +699,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull Breadcrumb deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull Breadcrumb deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { reader.beginObject(); @NotNull Date timestamp = DateUtils.getCurrentDateTime(); String message = null; diff --git a/sentry/src/main/java/io/sentry/CheckIn.java b/sentry/src/main/java/io/sentry/CheckIn.java index 4c83771324f..e7c6abef3e8 100644 --- a/sentry/src/main/java/io/sentry/CheckIn.java +++ b/sentry/src/main/java/io/sentry/CheckIn.java @@ -170,7 +170,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull CheckIn deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull CheckIn deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { SentryId sentryId = null; MonitorConfig monitorConfig = null; diff --git a/sentry/src/main/java/io/sentry/DataCategory.java b/sentry/src/main/java/io/sentry/DataCategory.java index 6b119b43e4c..3e54f9da4b0 100644 --- a/sentry/src/main/java/io/sentry/DataCategory.java +++ b/sentry/src/main/java/io/sentry/DataCategory.java @@ -13,6 +13,7 @@ public enum DataCategory { Monitor("monitor"), Profile("profile"), Transaction("transaction"), + Replay("replay"), Security("security"), UserReport("user_report"), Unknown("unknown"); diff --git a/sentry/src/main/java/io/sentry/EventProcessor.java b/sentry/src/main/java/io/sentry/EventProcessor.java index ba675086142..9e52408edbc 100644 --- a/sentry/src/main/java/io/sentry/EventProcessor.java +++ b/sentry/src/main/java/io/sentry/EventProcessor.java @@ -32,4 +32,16 @@ default SentryEvent process(@NotNull SentryEvent event, @NotNull Hint hint) { default SentryTransaction process(@NotNull SentryTransaction transaction, @NotNull Hint hint) { return transaction; } + + /** + * May mutate or drop a SentryEvent + * + * @param event the SentryEvent + * @param hint the Hint + * @return the event itself, a mutated SentryEvent or null + */ + @Nullable + default SentryReplayEvent process(@NotNull SentryReplayEvent event, @NotNull Hint hint) { + return event; + } } diff --git a/sentry/src/main/java/io/sentry/Hint.java b/sentry/src/main/java/io/sentry/Hint.java index 07dde3cb807..750017d00dd 100644 --- a/sentry/src/main/java/io/sentry/Hint.java +++ b/sentry/src/main/java/io/sentry/Hint.java @@ -29,8 +29,8 @@ public final class Hint { private final @NotNull List attachments = new ArrayList<>(); private @Nullable Attachment screenshot = null; private @Nullable Attachment viewHierarchy = null; - private @Nullable Attachment threadDump = null; + private @Nullable ReplayRecording replayRecording = null; public static @NotNull Hint withAttachment(@Nullable Attachment attachment) { @NotNull final Hint hint = new Hint(); @@ -136,6 +136,15 @@ public void setThreadDump(final @Nullable Attachment threadDump) { return threadDump; } + @Nullable + public ReplayRecording getReplayRecording() { + return replayRecording; + } + + public void setReplayRecording(final @Nullable ReplayRecording replayRecording) { + this.replayRecording = replayRecording; + } + private boolean isCastablePrimitive(@Nullable Object hintValue, @NotNull Class clazz) { Class nonPrimitiveClass = PRIMITIVE_MAPPINGS.get(clazz.getCanonicalName()); return hintValue != null diff --git a/sentry/src/main/java/io/sentry/Hub.java b/sentry/src/main/java/io/sentry/Hub.java index e7942722fc7..8861c1c67fb 100644 --- a/sentry/src/main/java/io/sentry/Hub.java +++ b/sentry/src/main/java/io/sentry/Hub.java @@ -920,6 +920,28 @@ private IScope buildLocalScope( return sentryId; } + @Override + public @NotNull SentryId captureReplay( + final @NotNull SentryReplayEvent replay, final @Nullable Hint hint) { + SentryId sentryId = SentryId.EMPTY_ID; + if (!isEnabled()) { + options + .getLogger() + .log( + SentryLevel.WARNING, + "Instance is disabled and this 'captureReplay' call is a no-op."); + } else { + try { + final @NotNull StackItem item = stack.peek(); + sentryId = item.getClient().captureReplayEvent(replay, item.getScope(), hint); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.ERROR, "Error while capturing replay", e); + } + } + this.lastEventId = sentryId; + return sentryId; + } + @ApiStatus.Internal @Override public @Nullable RateLimiter getRateLimiter() { diff --git a/sentry/src/main/java/io/sentry/HubAdapter.java b/sentry/src/main/java/io/sentry/HubAdapter.java index 68a9bdf11dd..5d7796f1642 100644 --- a/sentry/src/main/java/io/sentry/HubAdapter.java +++ b/sentry/src/main/java/io/sentry/HubAdapter.java @@ -262,6 +262,12 @@ public void reportFullyDisplayed() { return Sentry.captureCheckIn(checkIn); } + @Override + public @NotNull SentryId captureReplay( + final @NotNull SentryReplayEvent replay, final @Nullable Hint hint) { + return Sentry.getCurrentHub().captureReplay(replay, hint); + } + @ApiStatus.Internal @Override public @Nullable RateLimiter getRateLimiter() { diff --git a/sentry/src/main/java/io/sentry/IHub.java b/sentry/src/main/java/io/sentry/IHub.java index 01431043f09..f6b2eab81d5 100644 --- a/sentry/src/main/java/io/sentry/IHub.java +++ b/sentry/src/main/java/io/sentry/IHub.java @@ -572,6 +572,9 @@ TransactionContext continueTrace( @NotNull SentryId captureCheckIn(final @NotNull CheckIn checkIn); + @NotNull + SentryId captureReplay(@NotNull SentryReplayEvent replay, @Nullable Hint hint); + @ApiStatus.Internal @Nullable RateLimiter getRateLimiter(); diff --git a/sentry/src/main/java/io/sentry/ISentryClient.java b/sentry/src/main/java/io/sentry/ISentryClient.java index 15b5f25c4ba..e568746e9a1 100644 --- a/sentry/src/main/java/io/sentry/ISentryClient.java +++ b/sentry/src/main/java/io/sentry/ISentryClient.java @@ -147,6 +147,10 @@ public interface ISentryClient { return captureException(throwable, scope, null); } + @NotNull + SentryId captureReplayEvent( + @NotNull SentryReplayEvent event, @Nullable IScope scope, @Nullable Hint hint); + /** * Captures a manually created user feedback and sends it to Sentry. * diff --git a/sentry/src/main/java/io/sentry/JsonDeserializer.java b/sentry/src/main/java/io/sentry/JsonDeserializer.java index 7e62814fe64..390328231b6 100644 --- a/sentry/src/main/java/io/sentry/JsonDeserializer.java +++ b/sentry/src/main/java/io/sentry/JsonDeserializer.java @@ -6,5 +6,5 @@ @ApiStatus.Internal public interface JsonDeserializer { @NotNull - T deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception; + T deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception; } diff --git a/sentry/src/main/java/io/sentry/JsonObjectReader.java b/sentry/src/main/java/io/sentry/JsonObjectReader.java index 79c9a35420c..c069a9e26d8 100644 --- a/sentry/src/main/java/io/sentry/JsonObjectReader.java +++ b/sentry/src/main/java/io/sentry/JsonObjectReader.java @@ -15,64 +15,74 @@ import org.jetbrains.annotations.Nullable; @ApiStatus.Internal -public final class JsonObjectReader extends JsonReader { +public final class JsonObjectReader implements ObjectReader { + + private final @NotNull JsonReader jsonReader; public JsonObjectReader(Reader in) { - super(in); + this.jsonReader = new JsonReader(in); } + @Override public @Nullable String nextStringOrNull() throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } - return nextString(); + return jsonReader.nextString(); } + @Override public @Nullable Double nextDoubleOrNull() throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } - return nextDouble(); + return jsonReader.nextDouble(); } + @Override public @Nullable Float nextFloatOrNull() throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } return nextFloat(); } - public @NotNull Float nextFloat() throws IOException { - return (float) nextDouble(); + @Override + public float nextFloat() throws IOException { + return (float) jsonReader.nextDouble(); } + @Override public @Nullable Long nextLongOrNull() throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } - return nextLong(); + return jsonReader.nextLong(); } + @Override public @Nullable Integer nextIntegerOrNull() throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } - return nextInt(); + return jsonReader.nextInt(); } + @Override public @Nullable Boolean nextBooleanOrNull() throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } - return nextBoolean(); + return jsonReader.nextBoolean(); } + @Override public void nextUnknown(ILogger logger, Map unknown, String name) { try { unknown.put(name, nextObjectOrNull()); @@ -81,90 +91,79 @@ public void nextUnknown(ILogger logger, Map unknown, String name } } + @Override public @Nullable List nextListOrNull( @NotNull ILogger logger, @NotNull JsonDeserializer deserializer) throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } - beginArray(); + jsonReader.beginArray(); List list = new ArrayList<>(); - if (hasNext()) { + if (jsonReader.hasNext()) { do { try { list.add(deserializer.deserialize(this, logger)); } catch (Exception e) { logger.log(SentryLevel.WARNING, "Failed to deserialize object in list.", e); } - } while (peek() == JsonToken.BEGIN_OBJECT); + } while (jsonReader.peek() == JsonToken.BEGIN_OBJECT); } - endArray(); + jsonReader.endArray(); return list; } + @Override public @Nullable Map nextMapOrNull( @NotNull ILogger logger, @NotNull JsonDeserializer deserializer) throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } - beginObject(); + jsonReader.beginObject(); Map map = new HashMap<>(); - if (hasNext()) { + if (jsonReader.hasNext()) { do { try { - String key = nextName(); + String key = jsonReader.nextName(); map.put(key, deserializer.deserialize(this, logger)); } catch (Exception e) { logger.log(SentryLevel.WARNING, "Failed to deserialize object in map.", e); } - } while (peek() == JsonToken.BEGIN_OBJECT || peek() == JsonToken.NAME); + } while (jsonReader.peek() == JsonToken.BEGIN_OBJECT || jsonReader.peek() == JsonToken.NAME); } - endObject(); + jsonReader.endObject(); return map; } + @Override public @Nullable T nextOrNull( @NotNull ILogger logger, @NotNull JsonDeserializer deserializer) throws Exception { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } return deserializer.deserialize(this, logger); } + @Override public @Nullable Date nextDateOrNull(ILogger logger) throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); - return null; - } - return JsonObjectReader.dateOrNull(nextString(), logger); - } - - public static @Nullable Date dateOrNull(@Nullable String dateString, ILogger logger) { - if (dateString == null) { + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } - try { - return DateUtils.getDateTime(dateString); - } catch (Exception ignored) { - try { - return DateUtils.getDateTimeWithMillisPrecision(dateString); - } catch (Exception e) { - logger.log(SentryLevel.ERROR, "Error when deserializing millis timestamp format.", e); - } - } - return null; + return ObjectReader.dateOrNull(jsonReader.nextString(), logger); } + @Override public @Nullable TimeZone nextTimeZoneOrNull(ILogger logger) throws IOException { - if (peek() == JsonToken.NULL) { - nextNull(); + if (jsonReader.peek() == JsonToken.NULL) { + jsonReader.nextNull(); return null; } try { - return TimeZone.getTimeZone(nextString()); + return TimeZone.getTimeZone(jsonReader.nextString()); } catch (Exception e) { logger.log(SentryLevel.ERROR, "Error when deserializing TimeZone", e); } @@ -177,7 +176,88 @@ public void nextUnknown(ILogger logger, Map unknown, String name * * @return The deserialized object from json. */ + @Override public @Nullable Object nextObjectOrNull() throws IOException { return new JsonObjectDeserializer().deserialize(this); } + + @Override + public @NotNull JsonToken peek() throws IOException { + return jsonReader.peek(); + } + + @Override + public @NotNull String nextName() throws IOException { + return jsonReader.nextName(); + } + + @Override + public void beginObject() throws IOException { + jsonReader.beginObject(); + } + + @Override + public void endObject() throws IOException { + jsonReader.endObject(); + } + + @Override + public void beginArray() throws IOException { + jsonReader.beginArray(); + } + + @Override + public void endArray() throws IOException { + jsonReader.endArray(); + } + + @Override + public boolean hasNext() throws IOException { + return jsonReader.hasNext(); + } + + @Override + public int nextInt() throws IOException { + return jsonReader.nextInt(); + } + + @Override + public long nextLong() throws IOException { + return jsonReader.nextLong(); + } + + @Override + public String nextString() throws IOException { + return jsonReader.nextString(); + } + + @Override + public boolean nextBoolean() throws IOException { + return jsonReader.nextBoolean(); + } + + @Override + public double nextDouble() throws IOException { + return jsonReader.nextDouble(); + } + + @Override + public void nextNull() throws IOException { + jsonReader.nextNull(); + } + + @Override + public void setLenient(boolean lenient) { + jsonReader.setLenient(lenient); + } + + @Override + public void skipValue() throws IOException { + jsonReader.skipValue(); + } + + @Override + public void close() throws IOException { + jsonReader.close(); + } } diff --git a/sentry/src/main/java/io/sentry/JsonObjectWriter.java b/sentry/src/main/java/io/sentry/JsonObjectWriter.java index b174ddb4847..f1e84e6d5a0 100644 --- a/sentry/src/main/java/io/sentry/JsonObjectWriter.java +++ b/sentry/src/main/java/io/sentry/JsonObjectWriter.java @@ -52,6 +52,12 @@ public JsonObjectWriter value(final @Nullable String value) throws IOException { return this; } + @Override + public ObjectWriter jsonValue(@Nullable String value) throws IOException { + jsonWriter.jsonValue(value); + return this; + } + @Override public JsonObjectWriter nullValue() throws IOException { jsonWriter.nullValue(); @@ -103,6 +109,11 @@ public JsonObjectWriter value(final @NotNull ILogger logger, final @Nullable Obj return this; } + @Override + public void setLenient(final boolean lenient) { + jsonWriter.setLenient(lenient); + } + public void setIndent(final @NotNull String indent) { jsonWriter.setIndent(indent); } diff --git a/sentry/src/main/java/io/sentry/JsonSerializer.java b/sentry/src/main/java/io/sentry/JsonSerializer.java index c22eb095b5a..9f0e93f59a9 100644 --- a/sentry/src/main/java/io/sentry/JsonSerializer.java +++ b/sentry/src/main/java/io/sentry/JsonSerializer.java @@ -29,6 +29,9 @@ import io.sentry.protocol.User; import io.sentry.protocol.ViewHierarchy; import io.sentry.protocol.ViewHierarchyNode; +import io.sentry.rrweb.RRWebEventType; +import io.sentry.rrweb.RRWebMetaEvent; +import io.sentry.rrweb.RRWebVideoEvent; import io.sentry.util.Objects; import java.io.BufferedOutputStream; import java.io.BufferedWriter; @@ -89,6 +92,10 @@ public JsonSerializer(@NotNull SentryOptions options) { deserializersByClass.put( ProfileMeasurementValue.class, new ProfileMeasurementValue.Deserializer()); deserializersByClass.put(Request.class, new Request.Deserializer()); + deserializersByClass.put(ReplayRecording.class, new ReplayRecording.Deserializer()); + deserializersByClass.put(RRWebEventType.class, new RRWebEventType.Deserializer()); + deserializersByClass.put(RRWebMetaEvent.class, new RRWebMetaEvent.Deserializer()); + deserializersByClass.put(RRWebVideoEvent.class, new RRWebVideoEvent.Deserializer()); deserializersByClass.put(SdkInfo.class, new SdkInfo.Deserializer()); deserializersByClass.put(SdkVersion.class, new SdkVersion.Deserializer()); deserializersByClass.put(SentryEnvelopeHeader.class, new SentryEnvelopeHeader.Deserializer()); @@ -101,6 +108,7 @@ public JsonSerializer(@NotNull SentryOptions options) { deserializersByClass.put(SentryLockReason.class, new SentryLockReason.Deserializer()); deserializersByClass.put(SentryPackage.class, new SentryPackage.Deserializer()); deserializersByClass.put(SentryRuntime.class, new SentryRuntime.Deserializer()); + deserializersByClass.put(SentryReplayEvent.class, new SentryReplayEvent.Deserializer()); deserializersByClass.put(SentrySpan.class, new SentrySpan.Deserializer()); deserializersByClass.put(SentryStackFrame.class, new SentryStackFrame.Deserializer()); deserializersByClass.put(SentryStackTrace.class, new SentryStackTrace.Deserializer()); diff --git a/sentry/src/main/java/io/sentry/MainEventProcessor.java b/sentry/src/main/java/io/sentry/MainEventProcessor.java index abbf21c84e5..d6445e3a56d 100644 --- a/sentry/src/main/java/io/sentry/MainEventProcessor.java +++ b/sentry/src/main/java/io/sentry/MainEventProcessor.java @@ -149,6 +149,20 @@ private void processNonCachedEvent(final @NotNull SentryBaseEvent event) { return transaction; } + @Override + public @NotNull SentryReplayEvent process( + final @NotNull SentryReplayEvent event, final @NotNull Hint hint) { + setCommons(event); + // TODO: maybe later it's needed to deobfuscate something (e.g. view hierarchy), for now the + // TODO: protocol does not support it + // setDebugMeta(event); + + if (shouldApplyScopeData(event, hint)) { + processNonCachedEvent(event); + } + return event; + } + private void setCommons(final @NotNull SentryBaseEvent event) { setPlatform(event); } diff --git a/sentry/src/main/java/io/sentry/MonitorConfig.java b/sentry/src/main/java/io/sentry/MonitorConfig.java index c25884c2bde..7d15eb26d38 100644 --- a/sentry/src/main/java/io/sentry/MonitorConfig.java +++ b/sentry/src/main/java/io/sentry/MonitorConfig.java @@ -105,8 +105,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull MonitorConfig deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull MonitorConfig deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { MonitorSchedule schedule = null; Long checkinMargin = null; Long maxRuntime = null; diff --git a/sentry/src/main/java/io/sentry/MonitorContexts.java b/sentry/src/main/java/io/sentry/MonitorContexts.java index 3a15aa4113c..00ccb680fc3 100644 --- a/sentry/src/main/java/io/sentry/MonitorContexts.java +++ b/sentry/src/main/java/io/sentry/MonitorContexts.java @@ -66,7 +66,7 @@ public static final class Deserializer implements JsonDeserializer { @Override public @NotNull MonitorSchedule deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { String type = null; String value = null; String unit = null; diff --git a/sentry/src/main/java/io/sentry/NoOpHub.java b/sentry/src/main/java/io/sentry/NoOpHub.java index d186c69ca2f..b238704cc54 100644 --- a/sentry/src/main/java/io/sentry/NoOpHub.java +++ b/sentry/src/main/java/io/sentry/NoOpHub.java @@ -218,6 +218,11 @@ public void reportFullyDisplayed() {} return SentryId.EMPTY_ID; } + @Override + public @NotNull SentryId captureReplay(@NotNull SentryReplayEvent replay, @Nullable Hint hint) { + return SentryId.EMPTY_ID; + } + @Override public @Nullable RateLimiter getRateLimiter() { return null; diff --git a/sentry/src/main/java/io/sentry/NoOpSentryClient.java b/sentry/src/main/java/io/sentry/NoOpSentryClient.java index a37d09eb89f..24b43f2b842 100644 --- a/sentry/src/main/java/io/sentry/NoOpSentryClient.java +++ b/sentry/src/main/java/io/sentry/NoOpSentryClient.java @@ -62,6 +62,12 @@ public SentryId captureEnvelope(@NotNull SentryEnvelope envelope, @Nullable Hint return SentryId.EMPTY_ID; } + @Override + public @NotNull SentryId captureReplayEvent( + @NotNull SentryReplayEvent event, @Nullable IScope scope, @Nullable Hint hint) { + return SentryId.EMPTY_ID; + } + @Override public @Nullable RateLimiter getRateLimiter() { return null; diff --git a/sentry/src/main/java/io/sentry/ObjectReader.java b/sentry/src/main/java/io/sentry/ObjectReader.java new file mode 100644 index 00000000000..6c2210897e0 --- /dev/null +++ b/sentry/src/main/java/io/sentry/ObjectReader.java @@ -0,0 +1,102 @@ +package io.sentry; + +import io.sentry.vendor.gson.stream.JsonToken; +import java.io.Closeable; +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface ObjectReader extends Closeable { + static @Nullable Date dateOrNull( + final @Nullable String dateString, final @NotNull ILogger logger) { + if (dateString == null) { + return null; + } + try { + return DateUtils.getDateTime(dateString); + } catch (Exception ignored) { + try { + return DateUtils.getDateTimeWithMillisPrecision(dateString); + } catch (Exception e) { + logger.log(SentryLevel.ERROR, "Error when deserializing millis timestamp format.", e); + } + } + return null; + } + + void nextUnknown(ILogger logger, Map unknown, String name); + + @Nullable List nextListOrNull( + @NotNull ILogger logger, @NotNull JsonDeserializer deserializer) throws IOException; + + @Nullable Map nextMapOrNull( + @NotNull ILogger logger, @NotNull JsonDeserializer deserializer) throws IOException; + + @Nullable T nextOrNull(@NotNull ILogger logger, @NotNull JsonDeserializer deserializer) + throws Exception; + + @Nullable + Date nextDateOrNull(ILogger logger) throws IOException; + + @Nullable + TimeZone nextTimeZoneOrNull(ILogger logger) throws IOException; + + @Nullable + Object nextObjectOrNull() throws IOException; + + @NotNull + JsonToken peek() throws IOException; + + @NotNull + String nextName() throws IOException; + + void beginObject() throws IOException; + + void endObject() throws IOException; + + void beginArray() throws IOException; + + void endArray() throws IOException; + + boolean hasNext() throws IOException; + + int nextInt() throws IOException; + + @Nullable + Integer nextIntegerOrNull() throws IOException; + + long nextLong() throws IOException; + + @Nullable + Long nextLongOrNull() throws IOException; + + String nextString() throws IOException; + + @Nullable + String nextStringOrNull() throws IOException; + + boolean nextBoolean() throws IOException; + + @Nullable + Boolean nextBooleanOrNull() throws IOException; + + double nextDouble() throws IOException; + + @Nullable + Double nextDoubleOrNull() throws IOException; + + float nextFloat() throws IOException; + + @Nullable + Float nextFloatOrNull() throws IOException; + + void nextNull() throws IOException; + + void setLenient(boolean lenient); + + void skipValue() throws IOException; +} diff --git a/sentry/src/main/java/io/sentry/ObjectWriter.java b/sentry/src/main/java/io/sentry/ObjectWriter.java index ea8d4e83eac..91e64a0c8b5 100644 --- a/sentry/src/main/java/io/sentry/ObjectWriter.java +++ b/sentry/src/main/java/io/sentry/ObjectWriter.java @@ -17,6 +17,8 @@ public interface ObjectWriter { ObjectWriter value(final @Nullable String value) throws IOException; + ObjectWriter jsonValue(final @Nullable String value) throws IOException; + ObjectWriter nullValue() throws IOException; ObjectWriter value(final boolean value) throws IOException; @@ -31,4 +33,6 @@ public interface ObjectWriter { ObjectWriter value(final @NotNull ILogger logger, final @Nullable Object object) throws IOException; + + void setLenient(boolean lenient); } diff --git a/sentry/src/main/java/io/sentry/ProfilingTraceData.java b/sentry/src/main/java/io/sentry/ProfilingTraceData.java index 35902f5f04c..9d54a7ee0e6 100644 --- a/sentry/src/main/java/io/sentry/ProfilingTraceData.java +++ b/sentry/src/main/java/io/sentry/ProfilingTraceData.java @@ -448,7 +448,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; diff --git a/sentry/src/main/java/io/sentry/ProfilingTransactionData.java b/sentry/src/main/java/io/sentry/ProfilingTransactionData.java index 46ba9bba444..045b859f05f 100644 --- a/sentry/src/main/java/io/sentry/ProfilingTransactionData.java +++ b/sentry/src/main/java/io/sentry/ProfilingTransactionData.java @@ -179,7 +179,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; diff --git a/sentry/src/main/java/io/sentry/ReplayRecording.java b/sentry/src/main/java/io/sentry/ReplayRecording.java new file mode 100644 index 00000000000..4e6aecdb2bf --- /dev/null +++ b/sentry/src/main/java/io/sentry/ReplayRecording.java @@ -0,0 +1,182 @@ +package io.sentry; + +import io.sentry.rrweb.RRWebEvent; +import io.sentry.rrweb.RRWebEventType; +import io.sentry.rrweb.RRWebMetaEvent; +import io.sentry.rrweb.RRWebVideoEvent; +import io.sentry.util.MapObjectReader; +import io.sentry.util.Objects; +import io.sentry.vendor.gson.stream.JsonToken; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class ReplayRecording implements JsonUnknown, JsonSerializable { + + public static final class JsonKeys { + public static final String SEGMENT_ID = "segment_id"; + } + + private @Nullable Integer segmentId; + private @Nullable List payload; + private @Nullable Map unknown; + + @Nullable + public Integer getSegmentId() { + return segmentId; + } + + public void setSegmentId(final @Nullable Integer segmentId) { + this.segmentId = segmentId; + } + + @Nullable + public List getPayload() { + return payload; + } + + public void setPayload(final @Nullable List payload) { + this.payload = payload; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ReplayRecording that = (ReplayRecording) o; + return Objects.equals(segmentId, that.segmentId) && Objects.equals(payload, that.payload); + } + + @Override + public int hashCode() { + return Objects.hash(segmentId, payload); + } + + @Override + public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { + writer.beginObject(); + if (segmentId != null) { + writer.name(JsonKeys.SEGMENT_ID).value(segmentId); + } + + if (unknown != null) { + for (String key : unknown.keySet()) { + Object value = unknown.get(key); + writer.name(key).value(logger, value); + } + } + writer.endObject(); + + // {"segment_id":0}\n{json-serialized-rrweb-protocol} + + writer.setLenient(true); + writer.jsonValue("\n"); + if (payload != null) { + writer.value(logger, payload); + } + writer.setLenient(false); + } + + @Override + public @Nullable Map getUnknown() { + return unknown; + } + + @Override + public void setUnknown(@Nullable Map unknown) { + this.unknown = unknown; + } + + public static final class Deserializer implements JsonDeserializer { + + @SuppressWarnings("unchecked") + @Override + public @NotNull ReplayRecording deserialize( + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { + + final ReplayRecording replay = new ReplayRecording(); + + @Nullable Map unknown = null; + @Nullable Integer segmentId = null; + @Nullable List payload = null; + + reader.beginObject(); + while (reader.peek() == JsonToken.NAME) { + final String nextName = reader.nextName(); + switch (nextName) { + case JsonKeys.SEGMENT_ID: + segmentId = reader.nextIntegerOrNull(); + break; + default: + if (unknown == null) { + unknown = new HashMap<>(); + } + reader.nextUnknown(logger, unknown, nextName); + break; + } + } + reader.endObject(); + + // {"segment_id":0}\n{json-serialized-rrweb-protocol} + + reader.setLenient(true); + List events = (List) reader.nextObjectOrNull(); + reader.setLenient(false); + + // since we lose the type of an rrweb event at runtime, we have to recover it from a map + if (events != null) { + payload = new ArrayList<>(events.size()); + for (Object event : events) { + if (event instanceof Map) { + final Map eventMap = (Map) event; + final ObjectReader mapReader = new MapObjectReader(eventMap); + for (final Map.Entry entry : eventMap.entrySet()) { + final String key = entry.getKey(); + final Object value = entry.getValue(); + if (key.equals("type")) { + final RRWebEventType type = RRWebEventType.values()[(int) value]; + switch (type) { + case Meta: + final RRWebEvent metaEvent = + new RRWebMetaEvent.Deserializer().deserialize(mapReader, logger); + payload.add(metaEvent); + break; + case Custom: + final Map data = + (Map) eventMap.getOrDefault("data", Collections.emptyMap()); + final String tag = + (String) data.getOrDefault(RRWebEvent.JsonKeys.TAG, "default"); + switch (tag) { + case RRWebVideoEvent.EVENT_TAG: + final RRWebEvent videoEvent = + new RRWebVideoEvent.Deserializer().deserialize(mapReader, logger); + payload.add(videoEvent); + break; + default: + logger.log(SentryLevel.DEBUG, "Unsupported rrweb event type %s", type); + break; + } + break; + default: + logger.log(SentryLevel.DEBUG, "Unsupported rrweb event type %s", type); + break; + } + } + } + } + } + } + + replay.setSegmentId(segmentId); + replay.setPayload(payload); + replay.setUnknown(unknown); + return replay; + } + } +} diff --git a/sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java b/sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java index d98ec2c32ff..a9828792d77 100644 --- a/sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java +++ b/sentry/src/main/java/io/sentry/SentryAppStartProfilingOptions.java @@ -151,7 +151,7 @@ public static final class Deserializer @Override public @NotNull SentryAppStartProfilingOptions deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); SentryAppStartProfilingOptions options = new SentryAppStartProfilingOptions(); Map unknown = null; diff --git a/sentry/src/main/java/io/sentry/SentryBaseEvent.java b/sentry/src/main/java/io/sentry/SentryBaseEvent.java index c247342cc28..58435194a7b 100644 --- a/sentry/src/main/java/io/sentry/SentryBaseEvent.java +++ b/sentry/src/main/java/io/sentry/SentryBaseEvent.java @@ -395,7 +395,7 @@ public static final class Deserializer { public boolean deserializeValue( @NotNull SentryBaseEvent baseEvent, @NotNull String nextName, - @NotNull JsonObjectReader reader, + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { switch (nextName) { diff --git a/sentry/src/main/java/io/sentry/SentryClient.java b/sentry/src/main/java/io/sentry/SentryClient.java index 2973c1f8bce..129d415a8ac 100644 --- a/sentry/src/main/java/io/sentry/SentryClient.java +++ b/sentry/src/main/java/io/sentry/SentryClient.java @@ -246,6 +246,60 @@ private boolean shouldApplyScopeData(final @NotNull CheckIn event, final @NotNul return sentryId; } + @Override + public @NotNull SentryId captureReplayEvent( + @NotNull SentryReplayEvent event, final @Nullable IScope scope, @Nullable Hint hint) { + Objects.requireNonNull(event, "SessionReplay is required."); + + if (hint == null) { + hint = new Hint(); + } + + if (shouldApplyScopeData(event, hint)) { + applyScope(event, scope); + } + + options.getLogger().log(SentryLevel.DEBUG, "Capturing session replay: %s", event.getEventId()); + + SentryId sentryId = SentryId.EMPTY_ID; + if (event.getEventId() != null) { + sentryId = event.getEventId(); + } + + event = processReplayEvent(event, hint, options.getEventProcessors()); + + if (event == null) { + options.getLogger().log(SentryLevel.DEBUG, "Replay was dropped by Event processors."); + return SentryId.EMPTY_ID; + } + + try { + @Nullable TraceContext traceContext = null; + if (scope != null) { + final @Nullable ITransaction transaction = scope.getTransaction(); + if (transaction != null) { + traceContext = transaction.traceContext(); + } else { + final @NotNull PropagationContext propagationContext = + TracingUtils.maybeUpdateBaggage(scope, options); + traceContext = propagationContext.traceContext(); + } + } + + final SentryEnvelope envelope = buildEnvelope(event, hint.getReplayRecording(), traceContext); + + hint.clear(); + transport.send(envelope, hint); + } catch (IOException e) { + options.getLogger().log(SentryLevel.WARNING, e, "Capturing event %s failed.", sentryId); + + // if there was an error capturing the event, we return an emptyId + sentryId = SentryId.EMPTY_ID; + } + + return sentryId; + } + private void addScopeAttachmentsToHint(@Nullable IScope scope, @NotNull Hint hint) { if (scope != null) { hint.addAttachments(scope.getAttachments()); @@ -432,6 +486,40 @@ private SentryTransaction processTransaction( return transaction; } + @Nullable + private SentryReplayEvent processReplayEvent( + @NotNull SentryReplayEvent replayEvent, + final @NotNull Hint hint, + final @NotNull List eventProcessors) { + for (final EventProcessor processor : eventProcessors) { + try { + replayEvent = processor.process(replayEvent, hint); + } catch (Throwable e) { + options + .getLogger() + .log( + SentryLevel.ERROR, + e, + "An exception occurred while processing replay event by processor: %s", + processor.getClass().getName()); + } + + if (replayEvent == null) { + options + .getLogger() + .log( + SentryLevel.DEBUG, + "Replay event was dropped by a processor: %s", + processor.getClass().getName()); + options + .getClientReportRecorder() + .recordLostEvent(DiscardReason.EVENT_PROCESSOR, DataCategory.Replay); + break; + } + } + return replayEvent; + } + @Override public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { Objects.requireNonNull(userFeedback, "SentryEvent is required."); @@ -485,6 +573,24 @@ public void captureUserFeedback(final @NotNull UserFeedback userFeedback) { return new SentryEnvelope(envelopeHeader, envelopeItems); } + private @NotNull SentryEnvelope buildEnvelope( + final @NotNull SentryReplayEvent event, + final @Nullable ReplayRecording replayRecording, + final @Nullable TraceContext traceContext) { + final List envelopeItems = new ArrayList<>(); + + final SentryEnvelopeItem replayItem = + SentryEnvelopeItem.fromReplay( + options.getSerializer(), options.getLogger(), event, replayRecording); + envelopeItems.add(replayItem); + final SentryId sentryId = event.getEventId(); + + final SentryEnvelopeHeader envelopeHeader = + new SentryEnvelopeHeader(sentryId, options.getSdkVersion(), traceContext); + + return new SentryEnvelope(envelopeHeader, envelopeItems); + } + /** * Updates the session data based on the event, hint and scope data * @@ -806,6 +912,47 @@ public void captureSession(final @NotNull Session session, final @Nullable Hint return checkIn; } + private @NotNull SentryReplayEvent applyScope( + final @NotNull SentryReplayEvent replayEvent, final @Nullable IScope scope) { + // no breadcrumbs and extras for replay events + if (scope != null) { + if (replayEvent.getRequest() == null) { + replayEvent.setRequest(scope.getRequest()); + } + if (replayEvent.getUser() == null) { + replayEvent.setUser(scope.getUser()); + } + if (replayEvent.getTags() == null) { + replayEvent.setTags(new HashMap<>(scope.getTags())); + } else { + for (Map.Entry item : scope.getTags().entrySet()) { + if (!replayEvent.getTags().containsKey(item.getKey())) { + replayEvent.getTags().put(item.getKey(), item.getValue()); + } + } + } + final Contexts contexts = replayEvent.getContexts(); + for (Map.Entry entry : new Contexts(scope.getContexts()).entrySet()) { + if (!contexts.containsKey(entry.getKey())) { + contexts.put(entry.getKey(), entry.getValue()); + } + } + + // Set trace data from active span to connect replays with transactions + final ISpan span = scope.getSpan(); + if (replayEvent.getContexts().getTrace() == null) { + if (span == null) { + replayEvent + .getContexts() + .setTrace(TransactionContext.fromPropagationContext(scope.getPropagationContext())); + } else { + replayEvent.getContexts().setTrace(span.getSpanContext()); + } + } + } + return replayEvent; + } + private @NotNull T applyScope( final @NotNull T sentryBaseEvent, final @Nullable IScope scope) { if (scope != null) { diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeHeader.java b/sentry/src/main/java/io/sentry/SentryEnvelopeHeader.java index ceb7e7bdd55..3e9525d3072 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeHeader.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeHeader.java @@ -117,7 +117,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override public @NotNull SentryEnvelopeHeader deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); SentryId eventId = null; diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java index acd6b36aa91..ff162f44642 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItem.java @@ -20,7 +20,11 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.Callable; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -102,8 +106,7 @@ public final class SentryEnvelopeItem { } public static @NotNull SentryEnvelopeItem fromEvent( - final @NotNull ISerializer serializer, final @NotNull SentryBaseEvent event) - throws IOException { + final @NotNull ISerializer serializer, final @NotNull SentryBaseEvent event) { Objects.requireNonNull(serializer, "ISerializer is required."); Objects.requireNonNull(event, "SentryEvent is required."); @@ -342,6 +345,66 @@ public ClientReport getClientReport(final @NotNull ISerializer serializer) throw } } + public static SentryEnvelopeItem fromReplay( + final @NotNull ISerializer serializer, + final @NotNull ILogger logger, + final @NotNull SentryReplayEvent replayEvent, + final @Nullable ReplayRecording replayRecording) { + + final File replayVideo = replayEvent.getVideoFile(); + + final CachedItem cachedItem = + new CachedItem( + () -> { + try { + try (final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + final Writer writer = + new BufferedWriter(new OutputStreamWriter(stream, UTF_8))) { + final Map replayPayload = new HashMap<>(); + // first serialize replay event json bytes + serializer.serialize(replayEvent, writer); + replayPayload.put(SentryItemType.ReplayEvent.getItemType(), stream.toByteArray()); + stream.reset(); + + // next serialize replay recording + if (replayRecording != null) { + serializer.serialize(replayRecording, writer); + replayPayload.put( + SentryItemType.ReplayRecording.getItemType(), stream.toByteArray()); + stream.reset(); + } + + // next serialize replay video bytes from given file + if (replayVideo != null && replayVideo.exists()) { + final byte[] videoBytes = + readBytesFromFile( + replayVideo.getPath(), SentryReplayEvent.REPLAY_VIDEO_MAX_SIZE); + if (videoBytes.length > 0) { + replayPayload.put(SentryItemType.ReplayVideo.getItemType(), videoBytes); + } + } + + return serializeToMsgpack(replayPayload); + } + } catch (Throwable t) { + logger.log(SentryLevel.ERROR, "Could not serialize replay recording", t); + return null; + } finally { + if (replayVideo != null) { + replayVideo.delete(); + } + } + }); + + final SentryEnvelopeItemHeader itemHeader = + new SentryEnvelopeItemHeader( + SentryItemType.ReplayVideo, () -> cachedItem.getBytes().length, null, null); + + // avoid method refs on Android due to some issues with older AGP setups + // noinspection Convert2MethodRef + return new SentryEnvelopeItem(itemHeader, () -> cachedItem.getBytes()); + } + private static class CachedItem { private @Nullable byte[] bytes; private final @Nullable Callable dataFactory; @@ -361,4 +424,35 @@ public CachedItem(final @Nullable Callable dataFactory) { return bytes != null ? bytes : new byte[] {}; } } + + @SuppressWarnings({"UnnecessaryParentheses"}) + private static byte[] serializeToMsgpack(final @NotNull Map map) + throws IOException { + try (final ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + + // Write map header + baos.write((byte) (0x80 | map.size())); + + // Iterate over the map and serialize each key-value pair + for (final Map.Entry entry : map.entrySet()) { + // Pack the key as a string + final byte[] keyBytes = entry.getKey().getBytes(UTF_8); + final int keyLength = keyBytes.length; + // string up to 255 chars + baos.write((byte) (0xd9)); + baos.write((byte) (keyLength)); + baos.write(keyBytes); + + // Pack the value as a binary string + final byte[] valueBytes = entry.getValue(); + final int valueLength = valueBytes.length; + // We will always use the 4 bytes data length for simplicity. + baos.write((byte) (0xc6)); + baos.write(ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(valueLength).array()); + baos.write(valueBytes); + } + + return baos.toByteArray(); + } + } } diff --git a/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java b/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java index 1ca9a1c8c2f..6903d9b1bb9 100644 --- a/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java +++ b/sentry/src/main/java/io/sentry/SentryEnvelopeItemHeader.java @@ -130,7 +130,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override public @NotNull SentryEnvelopeItemHeader deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); String contentType = null; diff --git a/sentry/src/main/java/io/sentry/SentryEvent.java b/sentry/src/main/java/io/sentry/SentryEvent.java index 5bd1cf3877f..d370458acbf 100644 --- a/sentry/src/main/java/io/sentry/SentryEvent.java +++ b/sentry/src/main/java/io/sentry/SentryEvent.java @@ -311,8 +311,8 @@ public static final class Deserializer implements JsonDeserializer @SuppressWarnings("unchecked") @Override - public @NotNull SentryEvent deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SentryEvent deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { reader.beginObject(); SentryEvent event = new SentryEvent(); Map unknown = null; diff --git a/sentry/src/main/java/io/sentry/SentryItemType.java b/sentry/src/main/java/io/sentry/SentryItemType.java index c4535cb6a1b..b0d9c62e905 100644 --- a/sentry/src/main/java/io/sentry/SentryItemType.java +++ b/sentry/src/main/java/io/sentry/SentryItemType.java @@ -18,6 +18,7 @@ public enum SentryItemType implements JsonSerializable { ClientReport("client_report"), ReplayEvent("replay_event"), ReplayRecording("replay_recording"), + ReplayVideo("replay_video"), CheckIn("check_in"), Unknown("__unknown__"); // DataCategory.Unknown @@ -64,7 +65,7 @@ static final class Deserializer implements JsonDeserializer { @Override public @NotNull SentryItemType deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { return SentryItemType.valueOfLabel(reader.nextString().toLowerCase(Locale.ROOT)); } } diff --git a/sentry/src/main/java/io/sentry/SentryLevel.java b/sentry/src/main/java/io/sentry/SentryLevel.java index ac179c9831b..f1c6e04cb87 100644 --- a/sentry/src/main/java/io/sentry/SentryLevel.java +++ b/sentry/src/main/java/io/sentry/SentryLevel.java @@ -21,8 +21,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger static final class Deserializer implements JsonDeserializer { @Override - public @NotNull SentryLevel deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SentryLevel deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { return SentryLevel.valueOf(reader.nextString().toUpperCase(Locale.ROOT)); } } diff --git a/sentry/src/main/java/io/sentry/SentryLockReason.java b/sentry/src/main/java/io/sentry/SentryLockReason.java index f376317f6b5..bd04f48ab0c 100644 --- a/sentry/src/main/java/io/sentry/SentryLockReason.java +++ b/sentry/src/main/java/io/sentry/SentryLockReason.java @@ -147,7 +147,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; reader.beginObject(); diff --git a/sentry/src/main/java/io/sentry/SentryReplayEvent.java b/sentry/src/main/java/io/sentry/SentryReplayEvent.java new file mode 100644 index 00000000000..95623d2ff62 --- /dev/null +++ b/sentry/src/main/java/io/sentry/SentryReplayEvent.java @@ -0,0 +1,319 @@ +package io.sentry; + +import io.sentry.protocol.SentryId; +import io.sentry.util.Objects; +import io.sentry.vendor.gson.stream.JsonToken; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class SentryReplayEvent extends SentryBaseEvent + implements JsonUnknown, JsonSerializable { + + public enum ReplayType implements JsonSerializable { + SESSION, + BUFFER; + + @Override + public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { + writer.value(name().toLowerCase(Locale.ROOT)); + } + + public static final class Deserializer implements JsonDeserializer { + @Override + public @NotNull ReplayType deserialize( + final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { + return ReplayType.valueOf(reader.nextString().toUpperCase(Locale.ROOT)); + } + } + } + + public static final long REPLAY_VIDEO_MAX_SIZE = 10 * 1024 * 1024; + public static final String REPLAY_EVENT_TYPE = "replay_event"; + + private @Nullable File videoFile; + private @NotNull String type; + private @NotNull ReplayType replayType; + private @Nullable SentryId replayId; + private int segmentId; + private @NotNull Date timestamp; + private @Nullable Date replayStartTimestamp; + private @Nullable List urls; + private @Nullable List errorIds; + private @Nullable List traceIds; + private @Nullable Map unknown; + + public SentryReplayEvent() { + super(); + this.replayId = new SentryId(); + this.type = REPLAY_EVENT_TYPE; + this.replayType = ReplayType.SESSION; + this.errorIds = new ArrayList<>(); + this.traceIds = new ArrayList<>(); + this.urls = new ArrayList<>(); + timestamp = DateUtils.getCurrentDateTime(); + } + + @Nullable + public File getVideoFile() { + return videoFile; + } + + public void setVideoFile(final @Nullable File videoFile) { + this.videoFile = videoFile; + } + + @NotNull + public String getType() { + return type; + } + + public void setType(final @NotNull String type) { + this.type = type; + } + + @Nullable + public SentryId getReplayId() { + return replayId; + } + + public void setReplayId(final @Nullable SentryId replayId) { + this.replayId = replayId; + } + + public int getSegmentId() { + return segmentId; + } + + public void setSegmentId(final int segmentId) { + this.segmentId = segmentId; + } + + @NotNull + public Date getTimestamp() { + return timestamp; + } + + public void setTimestamp(final @NotNull Date timestamp) { + this.timestamp = timestamp; + } + + @Nullable + public Date getReplayStartTimestamp() { + return replayStartTimestamp; + } + + public void setReplayStartTimestamp(final @Nullable Date replayStartTimestamp) { + this.replayStartTimestamp = replayStartTimestamp; + } + + @Nullable + public List getUrls() { + return urls; + } + + public void setUrls(final @Nullable List urls) { + this.urls = urls; + } + + @Nullable + public List getErrorIds() { + return errorIds; + } + + public void setErrorIds(final @Nullable List errorIds) { + this.errorIds = errorIds; + } + + @Nullable + public List getTraceIds() { + return traceIds; + } + + public void setTraceIds(final @Nullable List traceIds) { + this.traceIds = traceIds; + } + + @NotNull + public ReplayType getReplayType() { + return replayType; + } + + public void setReplayType(final @NotNull ReplayType replayType) { + this.replayType = replayType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SentryReplayEvent that = (SentryReplayEvent) o; + return segmentId == that.segmentId + && Objects.equals(type, that.type) + && replayType == that.replayType + && Objects.equals(replayId, that.replayId) + && Objects.equals(urls, that.urls) + && Objects.equals(errorIds, that.errorIds) + && Objects.equals(traceIds, that.traceIds); + } + + @Override + public int hashCode() { + return Objects.hash(type, replayType, replayId, segmentId, urls, errorIds, traceIds); + } + + // region json + public static final class JsonKeys { + public static final String TYPE = "type"; + public static final String REPLAY_TYPE = "replay_type"; + public static final String REPLAY_ID = "replay_id"; + public static final String SEGMENT_ID = "segment_id"; + public static final String TIMESTAMP = "timestamp"; + public static final String REPLAY_START_TIMESTAMP = "replay_start_timestamp"; + public static final String URLS = "urls"; + public static final String ERROR_IDS = "error_ids"; + public static final String TRACE_IDS = "trace_ids"; + } + + @Override + @SuppressWarnings("JdkObsolete") + public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { + writer.beginObject(); + writer.name(JsonKeys.TYPE).value(type); + writer.name(JsonKeys.REPLAY_TYPE).value(logger, replayType); + writer.name(JsonKeys.SEGMENT_ID).value(segmentId); + writer.name(JsonKeys.TIMESTAMP).value(logger, timestamp); + if (replayId != null) { + writer.name(JsonKeys.REPLAY_ID).value(logger, replayId); + } + if (replayStartTimestamp != null) { + writer.name(JsonKeys.REPLAY_START_TIMESTAMP).value(logger, replayStartTimestamp); + } + if (urls != null) { + writer.name(JsonKeys.URLS).value(logger, urls); + } + if (errorIds != null) { + writer.name(JsonKeys.ERROR_IDS).value(logger, errorIds); + } + if (traceIds != null) { + writer.name(JsonKeys.TRACE_IDS).value(logger, traceIds); + } + + new SentryBaseEvent.Serializer().serialize(this, writer, logger); + + if (unknown != null) { + for (String key : unknown.keySet()) { + Object value = unknown.get(key); + writer.name(key).value(logger, value); + } + } + writer.endObject(); + } + + @Override + public @Nullable Map getUnknown() { + return unknown; + } + + @Override + public void setUnknown(final @Nullable Map unknown) { + this.unknown = unknown; + } + + public static final class Deserializer implements JsonDeserializer { + + @SuppressWarnings("unchecked") + @Override + public @NotNull SentryReplayEvent deserialize( + final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { + + final SentryBaseEvent.Deserializer baseEventDeserializer = new SentryBaseEvent.Deserializer(); + + final SentryReplayEvent replay = new SentryReplayEvent(); + + @Nullable Map unknown = null; + @Nullable String type = null; + @Nullable ReplayType replayType = null; + @Nullable SentryId replayId = null; + @Nullable Integer segmentId = null; + @Nullable Date timestamp = null; + @Nullable Date replayStartTimestamp = null; + @Nullable List urls = null; + @Nullable List errorIds = null; + @Nullable List traceIds = null; + + reader.beginObject(); + while (reader.peek() == JsonToken.NAME) { + final String nextName = reader.nextName(); + switch (nextName) { + case JsonKeys.TYPE: + type = reader.nextStringOrNull(); + break; + case JsonKeys.REPLAY_TYPE: + replayType = reader.nextOrNull(logger, new ReplayType.Deserializer()); + break; + case JsonKeys.REPLAY_ID: + replayId = reader.nextOrNull(logger, new SentryId.Deserializer()); + break; + case JsonKeys.SEGMENT_ID: + segmentId = reader.nextIntegerOrNull(); + break; + case JsonKeys.TIMESTAMP: + timestamp = reader.nextDateOrNull(logger); + break; + case JsonKeys.REPLAY_START_TIMESTAMP: + replayStartTimestamp = reader.nextDateOrNull(logger); + break; + case JsonKeys.URLS: + urls = (List) reader.nextObjectOrNull(); + break; + case JsonKeys.ERROR_IDS: + errorIds = (List) reader.nextObjectOrNull(); + break; + case JsonKeys.TRACE_IDS: + traceIds = (List) reader.nextObjectOrNull(); + break; + default: + if (!baseEventDeserializer.deserializeValue(replay, nextName, reader, logger)) { + if (unknown == null) { + unknown = new HashMap<>(); + } + reader.nextUnknown(logger, unknown, nextName); + } + break; + } + } + reader.endObject(); + + if (type != null) { + replay.setType(type); + } + if (replayType != null) { + replay.setReplayType(replayType); + } + if (segmentId != null) { + replay.setSegmentId(segmentId); + } + if (timestamp != null) { + replay.setTimestamp(timestamp); + } + replay.setReplayId(replayId); + replay.setReplayStartTimestamp(replayStartTimestamp); + replay.setUrls(urls); + replay.setErrorIds(errorIds); + replay.setTraceIds(traceIds); + replay.setUnknown(unknown); + return replay; + } + } + // endregion json +} diff --git a/sentry/src/main/java/io/sentry/Session.java b/sentry/src/main/java/io/sentry/Session.java index 500da919fe2..482b055b676 100644 --- a/sentry/src/main/java/io/sentry/Session.java +++ b/sentry/src/main/java/io/sentry/Session.java @@ -426,7 +426,7 @@ public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull Session deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull Session deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); diff --git a/sentry/src/main/java/io/sentry/SpanContext.java b/sentry/src/main/java/io/sentry/SpanContext.java index be428708cb1..5a43ff845e0 100644 --- a/sentry/src/main/java/io/sentry/SpanContext.java +++ b/sentry/src/main/java/io/sentry/SpanContext.java @@ -292,8 +292,8 @@ public void setUnknown(@Nullable Map unknown) { public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull SpanContext deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SpanContext deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { reader.beginObject(); SentryId traceId = null; SpanId spanId = null; diff --git a/sentry/src/main/java/io/sentry/SpanId.java b/sentry/src/main/java/io/sentry/SpanId.java index 7e221775ced..70608fb7cbb 100644 --- a/sentry/src/main/java/io/sentry/SpanId.java +++ b/sentry/src/main/java/io/sentry/SpanId.java @@ -53,7 +53,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull SpanId deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull SpanId deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { return new SpanId(reader.nextString()); } diff --git a/sentry/src/main/java/io/sentry/SpanStatus.java b/sentry/src/main/java/io/sentry/SpanStatus.java index b0b1bf78c8c..5185d27e058 100644 --- a/sentry/src/main/java/io/sentry/SpanStatus.java +++ b/sentry/src/main/java/io/sentry/SpanStatus.java @@ -114,8 +114,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull SpanStatus deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SpanStatus deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { return SpanStatus.valueOf(reader.nextString().toUpperCase(Locale.ROOT)); } } diff --git a/sentry/src/main/java/io/sentry/TraceContext.java b/sentry/src/main/java/io/sentry/TraceContext.java index ef2944a9e9c..df799aaa07b 100644 --- a/sentry/src/main/java/io/sentry/TraceContext.java +++ b/sentry/src/main/java/io/sentry/TraceContext.java @@ -141,7 +141,7 @@ public static final class JsonKeys { public static final class Deserializer implements JsonDeserializer { @Override public @NotNull TraceContextUser deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); String id = null; @@ -239,8 +239,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull TraceContext deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull TraceContext deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { reader.beginObject(); SentryId traceId = null; diff --git a/sentry/src/main/java/io/sentry/UserFeedback.java b/sentry/src/main/java/io/sentry/UserFeedback.java index 27086188fe9..b580744ee77 100644 --- a/sentry/src/main/java/io/sentry/UserFeedback.java +++ b/sentry/src/main/java/io/sentry/UserFeedback.java @@ -174,8 +174,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull UserFeedback deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull UserFeedback deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { SentryId sentryId = null; String name = null; String email = null; diff --git a/sentry/src/main/java/io/sentry/clientreport/ClientReport.java b/sentry/src/main/java/io/sentry/clientreport/ClientReport.java index 66c3188116a..e1b8abcaea3 100644 --- a/sentry/src/main/java/io/sentry/clientreport/ClientReport.java +++ b/sentry/src/main/java/io/sentry/clientreport/ClientReport.java @@ -3,9 +3,9 @@ import io.sentry.DateUtils; import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryLevel; import io.sentry.vendor.gson.stream.JsonToken; @@ -74,8 +74,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull ClientReport deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull ClientReport deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { Date timestamp = null; List discardedEvents = new ArrayList<>(); Map unknown = null; diff --git a/sentry/src/main/java/io/sentry/clientreport/DiscardedEvent.java b/sentry/src/main/java/io/sentry/clientreport/DiscardedEvent.java index 8fb5da3165f..10b12b0fed5 100644 --- a/sentry/src/main/java/io/sentry/clientreport/DiscardedEvent.java +++ b/sentry/src/main/java/io/sentry/clientreport/DiscardedEvent.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryLevel; import io.sentry.vendor.gson.stream.JsonToken; @@ -93,7 +93,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override public @NotNull DiscardedEvent deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { String reason = null; String category = null; Long quanity = null; diff --git a/sentry/src/main/java/io/sentry/profilemeasurements/ProfileMeasurement.java b/sentry/src/main/java/io/sentry/profilemeasurements/ProfileMeasurement.java index 94e77edbfb7..1e6ff5fb41c 100644 --- a/sentry/src/main/java/io/sentry/profilemeasurements/ProfileMeasurement.java +++ b/sentry/src/main/java/io/sentry/profilemeasurements/ProfileMeasurement.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.Objects; import io.sentry.vendor.gson.stream.JsonToken; @@ -118,7 +118,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; diff --git a/sentry/src/main/java/io/sentry/profilemeasurements/ProfileMeasurementValue.java b/sentry/src/main/java/io/sentry/profilemeasurements/ProfileMeasurementValue.java index 9639ba892ff..b0cebf5439d 100644 --- a/sentry/src/main/java/io/sentry/profilemeasurements/ProfileMeasurementValue.java +++ b/sentry/src/main/java/io/sentry/profilemeasurements/ProfileMeasurementValue.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.Objects; import io.sentry.vendor.gson.stream.JsonToken; @@ -92,7 +92,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; diff --git a/sentry/src/main/java/io/sentry/protocol/App.java b/sentry/src/main/java/io/sentry/protocol/App.java index b7b41638db2..90d7d7aff6b 100644 --- a/sentry/src/main/java/io/sentry/protocol/App.java +++ b/sentry/src/main/java/io/sentry/protocol/App.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.util.Objects; @@ -255,7 +255,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull App deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull App deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); App app = new App(); diff --git a/sentry/src/main/java/io/sentry/protocol/Browser.java b/sentry/src/main/java/io/sentry/protocol/Browser.java index 99fe427c278..ed32be5ea2e 100644 --- a/sentry/src/main/java/io/sentry/protocol/Browser.java +++ b/sentry/src/main/java/io/sentry/protocol/Browser.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.util.Objects; @@ -102,7 +102,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull Browser deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull Browser deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); Browser browser = new Browser(); diff --git a/sentry/src/main/java/io/sentry/protocol/Contexts.java b/sentry/src/main/java/io/sentry/protocol/Contexts.java index 21be9fd8a58..28d2e8d2a44 100644 --- a/sentry/src/main/java/io/sentry/protocol/Contexts.java +++ b/sentry/src/main/java/io/sentry/protocol/Contexts.java @@ -2,8 +2,8 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SpanContext; import io.sentry.util.HintUtils; @@ -160,7 +160,7 @@ public static final class Deserializer implements JsonDeserializer { @Override public @NotNull Contexts deserialize( - final @NotNull JsonObjectReader reader, final @NotNull ILogger logger) throws Exception { + final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { final Contexts contexts = new Contexts(); reader.beginObject(); while (reader.peek() == JsonToken.NAME) { diff --git a/sentry/src/main/java/io/sentry/protocol/DebugImage.java b/sentry/src/main/java/io/sentry/protocol/DebugImage.java index d26432033e4..e769e2c2ca3 100644 --- a/sentry/src/main/java/io/sentry/protocol/DebugImage.java +++ b/sentry/src/main/java/io/sentry/protocol/DebugImage.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; @@ -314,8 +314,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull DebugImage deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull DebugImage deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { DebugImage debugImage = new DebugImage(); Map unknown = null; diff --git a/sentry/src/main/java/io/sentry/protocol/DebugMeta.java b/sentry/src/main/java/io/sentry/protocol/DebugMeta.java index 134947507ae..458c4de6311 100644 --- a/sentry/src/main/java/io/sentry/protocol/DebugMeta.java +++ b/sentry/src/main/java/io/sentry/protocol/DebugMeta.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; @@ -95,7 +95,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull DebugMeta deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull DebugMeta deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { DebugMeta debugMeta = new DebugMeta(); diff --git a/sentry/src/main/java/io/sentry/protocol/Device.java b/sentry/src/main/java/io/sentry/protocol/Device.java index 4f06f749953..25cfa41fd13 100644 --- a/sentry/src/main/java/io/sentry/protocol/Device.java +++ b/sentry/src/main/java/io/sentry/protocol/Device.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.util.Objects; @@ -544,7 +544,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override public @NotNull DeviceOrientation deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { return DeviceOrientation.valueOf(reader.nextString().toUpperCase(Locale.ROOT)); } } @@ -726,7 +726,7 @@ public void setUnknown(@Nullable Map unknown) { public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull Device deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull Device deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); Device device = new Device(); diff --git a/sentry/src/main/java/io/sentry/protocol/Geo.java b/sentry/src/main/java/io/sentry/protocol/Geo.java index fefc340e1be..6042b72d1d9 100644 --- a/sentry/src/main/java/io/sentry/protocol/Geo.java +++ b/sentry/src/main/java/io/sentry/protocol/Geo.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; @@ -161,7 +161,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public Geo deserialize(JsonObjectReader reader, ILogger logger) throws Exception { + public Geo deserialize(@NotNull ObjectReader reader, ILogger logger) throws Exception { reader.beginObject(); final Geo geo = new Geo(); Map unknown = null; diff --git a/sentry/src/main/java/io/sentry/protocol/Gpu.java b/sentry/src/main/java/io/sentry/protocol/Gpu.java index 0dfe85f68f9..b4a8344e2d7 100644 --- a/sentry/src/main/java/io/sentry/protocol/Gpu.java +++ b/sentry/src/main/java/io/sentry/protocol/Gpu.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.util.Objects; @@ -229,7 +229,7 @@ public void setUnknown(@Nullable Map unknown) { public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull Gpu deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull Gpu deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); Gpu gpu = new Gpu(); diff --git a/sentry/src/main/java/io/sentry/protocol/MeasurementValue.java b/sentry/src/main/java/io/sentry/protocol/MeasurementValue.java index 5ba28f41268..85cf6cf40f7 100644 --- a/sentry/src/main/java/io/sentry/protocol/MeasurementValue.java +++ b/sentry/src/main/java/io/sentry/protocol/MeasurementValue.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryLevel; import io.sentry.vendor.gson.stream.JsonToken; @@ -101,7 +101,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override public @NotNull MeasurementValue deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); String unit = null; diff --git a/sentry/src/main/java/io/sentry/protocol/Mechanism.java b/sentry/src/main/java/io/sentry/protocol/Mechanism.java index 648aed39c2b..fac8808f2db 100644 --- a/sentry/src/main/java/io/sentry/protocol/Mechanism.java +++ b/sentry/src/main/java/io/sentry/protocol/Mechanism.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.vendor.gson.stream.JsonToken; @@ -205,7 +205,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull Mechanism deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull Mechanism deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { Mechanism mechanism = new Mechanism(); Map unknown = null; diff --git a/sentry/src/main/java/io/sentry/protocol/Message.java b/sentry/src/main/java/io/sentry/protocol/Message.java index a1c79e21986..9aceea56a65 100644 --- a/sentry/src/main/java/io/sentry/protocol/Message.java +++ b/sentry/src/main/java/io/sentry/protocol/Message.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.vendor.gson.stream.JsonToken; @@ -131,7 +131,7 @@ public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull Message deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull Message deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); Message message = new Message(); diff --git a/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java b/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java index 796a4ea1a0f..ecfb59542b3 100644 --- a/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java +++ b/sentry/src/main/java/io/sentry/protocol/OperatingSystem.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.util.Objects; @@ -180,7 +180,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; diff --git a/sentry/src/main/java/io/sentry/protocol/Request.java b/sentry/src/main/java/io/sentry/protocol/Request.java index 14f54038444..44e205a3901 100644 --- a/sentry/src/main/java/io/sentry/protocol/Request.java +++ b/sentry/src/main/java/io/sentry/protocol/Request.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.util.Objects; @@ -326,7 +326,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger @SuppressWarnings("unchecked") public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull Request deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull Request deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); Request request = new Request(); diff --git a/sentry/src/main/java/io/sentry/protocol/Response.java b/sentry/src/main/java/io/sentry/protocol/Response.java index 23a16c78f8c..f1a93037109 100644 --- a/sentry/src/main/java/io/sentry/protocol/Response.java +++ b/sentry/src/main/java/io/sentry/protocol/Response.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.vendor.gson.stream.JsonToken; @@ -154,7 +154,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override public @NotNull Response deserialize( - final @NotNull JsonObjectReader reader, final @NotNull ILogger logger) throws Exception { + final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { reader.beginObject(); final Response response = new Response(); Map unknown = null; diff --git a/sentry/src/main/java/io/sentry/protocol/SdkInfo.java b/sentry/src/main/java/io/sentry/protocol/SdkInfo.java index ee3ac1eb165..928a8b522dc 100644 --- a/sentry/src/main/java/io/sentry/protocol/SdkInfo.java +++ b/sentry/src/main/java/io/sentry/protocol/SdkInfo.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; @@ -116,7 +116,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull SdkInfo deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull SdkInfo deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { SdkInfo sdkInfo = new SdkInfo(); diff --git a/sentry/src/main/java/io/sentry/protocol/SdkVersion.java b/sentry/src/main/java/io/sentry/protocol/SdkVersion.java index f7ba230463b..aa997910be7 100644 --- a/sentry/src/main/java/io/sentry/protocol/SdkVersion.java +++ b/sentry/src/main/java/io/sentry/protocol/SdkVersion.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryIntegrationPackageStorage; import io.sentry.SentryLevel; @@ -224,8 +224,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger @SuppressWarnings("unchecked") public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull SdkVersion deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SdkVersion deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { String name = null; String version = null; diff --git a/sentry/src/main/java/io/sentry/protocol/SentryException.java b/sentry/src/main/java/io/sentry/protocol/SentryException.java index 5ee9464a3c4..4d56e127474 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryException.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryException.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; @@ -223,7 +223,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; reader.beginObject(); diff --git a/sentry/src/main/java/io/sentry/protocol/SentryId.java b/sentry/src/main/java/io/sentry/protocol/SentryId.java index c1e5ea1819e..109655fdf2b 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryId.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryId.java @@ -2,8 +2,8 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.StringUtils; import java.io.IOException; @@ -82,7 +82,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull SentryId deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull SentryId deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { return new SentryId(reader.nextString()); } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryPackage.java b/sentry/src/main/java/io/sentry/protocol/SentryPackage.java index cea6bb84974..aa2358d8dfb 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryPackage.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryPackage.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryLevel; import io.sentry.util.Objects; @@ -100,8 +100,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull SentryPackage deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SentryPackage deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { String name = null; String version = null; diff --git a/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java b/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java index 751e664ae64..7d2ed8fa1ef 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryRuntime.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.vendor.gson.stream.JsonToken; @@ -110,8 +110,8 @@ public void setUnknown(@Nullable Map unknown) { public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull SentryRuntime deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SentryRuntime deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { reader.beginObject(); SentryRuntime runtime = new SentryRuntime(); Map unknown = null; diff --git a/sentry/src/main/java/io/sentry/protocol/SentrySpan.java b/sentry/src/main/java/io/sentry/protocol/SentrySpan.java index b627aa4c9f5..8e4396ac444 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentrySpan.java +++ b/sentry/src/main/java/io/sentry/protocol/SentrySpan.java @@ -3,9 +3,9 @@ import io.sentry.DateUtils; import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryLevel; import io.sentry.Span; @@ -218,8 +218,8 @@ public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull SentrySpan deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SentrySpan deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { reader.beginObject(); Double startTimestamp = null; diff --git a/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java b/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java index fcb93eb2e8f..03d64e2172f 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryLockReason; import io.sentry.vendor.gson.stream.JsonToken; @@ -398,7 +398,7 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @Override public @NotNull SentryStackFrame deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + @NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { SentryStackFrame sentryStackFrame = new SentryStackFrame(); Map unknown = null; reader.beginObject(); diff --git a/sentry/src/main/java/io/sentry/protocol/SentryStackTrace.java b/sentry/src/main/java/io/sentry/protocol/SentryStackTrace.java index 90b42666c8f..e79e8e7ec05 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryStackTrace.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryStackTrace.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.util.CollectionUtils; import io.sentry.vendor.gson.stream.JsonToken; @@ -154,7 +154,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; reader.beginObject(); diff --git a/sentry/src/main/java/io/sentry/protocol/SentryThread.java b/sentry/src/main/java/io/sentry/protocol/SentryThread.java index 1d57e35b10d..accb05968e1 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryThread.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryThread.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryLockReason; import io.sentry.vendor.gson.stream.JsonToken; @@ -303,8 +303,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull SentryThread deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull SentryThread deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { SentryThread sentryThread = new SentryThread(); Map unknown = null; reader.beginObject(); diff --git a/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java b/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java index d0c9271a26e..791fda17999 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryTransaction.java @@ -3,9 +3,9 @@ import io.sentry.DateUtils; import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.SentryBaseEvent; import io.sentry.SentryTracer; @@ -231,7 +231,7 @@ public static final class Deserializer implements JsonDeserializer { @SuppressWarnings("unchecked") @Override - public @NotNull User deserialize(@NotNull JsonObjectReader reader, @NotNull ILogger logger) + public @NotNull User deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) throws Exception { reader.beginObject(); User user = new User(); diff --git a/sentry/src/main/java/io/sentry/protocol/ViewHierarchy.java b/sentry/src/main/java/io/sentry/protocol/ViewHierarchy.java index 69e51560402..791c9bbbd69 100644 --- a/sentry/src/main/java/io/sentry/protocol/ViewHierarchy.java +++ b/sentry/src/main/java/io/sentry/protocol/ViewHierarchy.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; @@ -73,8 +73,8 @@ public void setUnknown(@Nullable Map unknown) { public static final class Deserializer implements JsonDeserializer { @Override - public @NotNull ViewHierarchy deserialize( - @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + public @NotNull ViewHierarchy deserialize(@NotNull ObjectReader reader, @NotNull ILogger logger) + throws Exception { @Nullable String renderingSystem = null; @Nullable List windows = null; diff --git a/sentry/src/main/java/io/sentry/protocol/ViewHierarchyNode.java b/sentry/src/main/java/io/sentry/protocol/ViewHierarchyNode.java index 923eb95877e..525d644fdc5 100644 --- a/sentry/src/main/java/io/sentry/protocol/ViewHierarchyNode.java +++ b/sentry/src/main/java/io/sentry/protocol/ViewHierarchyNode.java @@ -2,9 +2,9 @@ import io.sentry.ILogger; import io.sentry.JsonDeserializer; -import io.sentry.JsonObjectReader; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; import io.sentry.ObjectWriter; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; @@ -205,7 +205,7 @@ public static final class Deserializer implements JsonDeserializer unknown = null; @NotNull final ViewHierarchyNode node = new ViewHierarchyNode(); diff --git a/sentry/src/main/java/io/sentry/rrweb/RRWebEvent.java b/sentry/src/main/java/io/sentry/rrweb/RRWebEvent.java new file mode 100644 index 00000000000..07b2b9a70fe --- /dev/null +++ b/sentry/src/main/java/io/sentry/rrweb/RRWebEvent.java @@ -0,0 +1,94 @@ +package io.sentry.rrweb; + +import io.sentry.ILogger; +import io.sentry.ObjectReader; +import io.sentry.ObjectWriter; +import io.sentry.util.Objects; +import java.io.IOException; +import org.jetbrains.annotations.NotNull; + +public abstract class RRWebEvent { + + private @NotNull RRWebEventType type; + private long timestamp; + + protected RRWebEvent(final @NotNull RRWebEventType type) { + this.type = type; + this.timestamp = System.currentTimeMillis(); + } + + protected RRWebEvent() { + this(RRWebEventType.Custom); + } + + @NotNull + public RRWebEventType getType() { + return type; + } + + public void setType(final @NotNull RRWebEventType type) { + this.type = type; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(final long timestamp) { + this.timestamp = timestamp; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof RRWebEvent)) return false; + RRWebEvent that = (RRWebEvent) o; + return timestamp == that.timestamp && type == that.type; + } + + @Override + public int hashCode() { + return Objects.hash(type, timestamp); + } + + // region json + public static final class JsonKeys { + public static final String TYPE = "type"; + public static final String TIMESTAMP = "timestamp"; + public static final String TAG = "tag"; + } + + public static final class Serializer { + public void serialize( + final @NotNull RRWebEvent baseEvent, + final @NotNull ObjectWriter writer, + final @NotNull ILogger logger) + throws IOException { + writer.name(JsonKeys.TYPE).value(logger, baseEvent.type); + writer.name(JsonKeys.TIMESTAMP).value(baseEvent.timestamp); + } + } + + public static final class Deserializer { + @SuppressWarnings("unchecked") + public boolean deserializeValue( + final @NotNull RRWebEvent baseEvent, + final @NotNull String nextName, + final @NotNull ObjectReader reader, + final @NotNull ILogger logger) + throws Exception { + switch (nextName) { + case JsonKeys.TYPE: + baseEvent.type = + Objects.requireNonNull( + reader.nextOrNull(logger, new RRWebEventType.Deserializer()), ""); + return true; + case JsonKeys.TIMESTAMP: + baseEvent.timestamp = reader.nextLong(); + return true; + } + return false; + } + } + // endregion json +} diff --git a/sentry/src/main/java/io/sentry/rrweb/RRWebEventType.java b/sentry/src/main/java/io/sentry/rrweb/RRWebEventType.java new file mode 100644 index 00000000000..fc9c8c7e690 --- /dev/null +++ b/sentry/src/main/java/io/sentry/rrweb/RRWebEventType.java @@ -0,0 +1,33 @@ +package io.sentry.rrweb; + +import io.sentry.ILogger; +import io.sentry.JsonDeserializer; +import io.sentry.JsonSerializable; +import io.sentry.ObjectReader; +import io.sentry.ObjectWriter; +import java.io.IOException; +import org.jetbrains.annotations.NotNull; + +public enum RRWebEventType implements JsonSerializable { + DomContentLoaded, + Load, + FullSnapshot, + IncrementalSnapshot, + Meta, + Custom, + Plugin; + + @Override + public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { + writer.value(ordinal()); + } + + public static final class Deserializer implements JsonDeserializer { + @Override + public @NotNull RRWebEventType deserialize( + final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { + return RRWebEventType.values()[reader.nextInt()]; + } + } +} diff --git a/sentry/src/main/java/io/sentry/rrweb/RRWebMetaEvent.java b/sentry/src/main/java/io/sentry/rrweb/RRWebMetaEvent.java new file mode 100644 index 00000000000..b0aca2f3374 --- /dev/null +++ b/sentry/src/main/java/io/sentry/rrweb/RRWebMetaEvent.java @@ -0,0 +1,191 @@ +package io.sentry.rrweb; + +import io.sentry.ILogger; +import io.sentry.JsonDeserializer; +import io.sentry.JsonSerializable; +import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; +import io.sentry.ObjectWriter; +import io.sentry.util.Objects; +import io.sentry.vendor.gson.stream.JsonToken; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class RRWebMetaEvent extends RRWebEvent implements JsonUnknown, JsonSerializable { + + private @NotNull String href; + private int height; + private int width; + // to support unknown json attributes with nesting, we have to have unknown map for each of the + // nested object in json: { ..., "data": { ... } } + private @Nullable Map unknown; + private @Nullable Map dataUnknown; + + public RRWebMetaEvent() { + super(RRWebEventType.Meta); + this.href = ""; + } + + @NotNull + public String getHref() { + return href; + } + + public void setHref(final @NotNull String href) { + this.href = href; + } + + public int getHeight() { + return height; + } + + public void setHeight(final int height) { + this.height = height; + } + + public int getWidth() { + return width; + } + + public void setWidth(final int width) { + this.width = width; + } + + @Nullable + public Map getDataUnknown() { + return dataUnknown; + } + + public void setDataUnknown(final @Nullable Map dataUnknown) { + this.dataUnknown = dataUnknown; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + RRWebMetaEvent metaEvent = (RRWebMetaEvent) o; + return height == metaEvent.height + && width == metaEvent.width + && Objects.equals(href, metaEvent.href); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), href, height, width); + } + + public static final class JsonKeys { + public static final String DATA = "data"; + public static final String HREF = "href"; + public static final String HEIGHT = "height"; + public static final String WIDTH = "width"; + } + + @Override + public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) throws IOException { + writer.beginObject(); + new RRWebEvent.Serializer().serialize(this, writer, logger); + writer.name(JsonKeys.DATA); + serializeData(writer, logger); + writer.endObject(); + } + + private void serializeData(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { + writer.beginObject(); + writer.name(JsonKeys.HREF).value(href); + writer.name(JsonKeys.HEIGHT).value(height); + writer.name(JsonKeys.WIDTH).value(width); + if (unknown != null) { + for (String key : unknown.keySet()) { + Object value = unknown.get(key); + writer.name(key); + writer.value(logger, value); + } + } + writer.endObject(); + } + + @Override + public @Nullable Map getUnknown() { + return unknown; + } + + @Override + public void setUnknown(final @Nullable Map unknown) { + this.unknown = unknown; + } + + public static final class Deserializer implements JsonDeserializer { + + @SuppressWarnings("unchecked") + @Override + public @NotNull RRWebMetaEvent deserialize( + final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { + reader.beginObject(); + @Nullable Map unknown = null; + final RRWebMetaEvent event = new RRWebMetaEvent(); + final RRWebEvent.Deserializer baseEventDeserializer = new RRWebEvent.Deserializer(); + + while (reader.peek() == JsonToken.NAME) { + final String nextName = reader.nextName(); + switch (nextName) { + case JsonKeys.DATA: + deserializeData(event, reader, logger); + break; + default: + if (!baseEventDeserializer.deserializeValue(event, nextName, reader, logger)) { + if (unknown == null) { + unknown = new HashMap<>(); + } + reader.nextUnknown(logger, unknown, nextName); + } + break; + } + } + event.setUnknown(unknown); + reader.endObject(); + return event; + } + + private void deserializeData( + final @NotNull RRWebMetaEvent event, + final @NotNull ObjectReader reader, + final @NotNull ILogger logger) + throws Exception { + @Nullable Map unknown = null; + + reader.beginObject(); + while (reader.peek() == JsonToken.NAME) { + final String nextName = reader.nextName(); + switch (nextName) { + case JsonKeys.HREF: + final String href = reader.nextStringOrNull(); + event.href = href == null ? "" : href; + break; + case JsonKeys.HEIGHT: + final Integer height = reader.nextIntegerOrNull(); + event.height = height == null ? 0 : height; + break; + case JsonKeys.WIDTH: + final Integer width = reader.nextIntegerOrNull(); + event.width = width == null ? 0 : width; + break; + default: + if (unknown == null) { + unknown = new ConcurrentHashMap<>(); + } + reader.nextUnknown(logger, unknown, nextName); + } + } + event.setDataUnknown(unknown); + reader.endObject(); + } + } +} diff --git a/sentry/src/main/java/io/sentry/rrweb/RRWebVideoEvent.java b/sentry/src/main/java/io/sentry/rrweb/RRWebVideoEvent.java new file mode 100644 index 00000000000..5bea9e3c471 --- /dev/null +++ b/sentry/src/main/java/io/sentry/rrweb/RRWebVideoEvent.java @@ -0,0 +1,433 @@ +package io.sentry.rrweb; + +import io.sentry.ILogger; +import io.sentry.JsonDeserializer; +import io.sentry.JsonSerializable; +import io.sentry.JsonUnknown; +import io.sentry.ObjectReader; +import io.sentry.ObjectWriter; +import io.sentry.util.Objects; +import io.sentry.vendor.gson.stream.JsonToken; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public final class RRWebVideoEvent extends RRWebEvent implements JsonUnknown, JsonSerializable { + + public static final String EVENT_TAG = "video"; + public static final String REPLAY_ENCODING = "h264"; + public static final String REPLAY_CONTAINER = "mp4"; + public static final String REPLAY_FRAME_RATE_TYPE_CONSTANT = "constant"; + public static final String REPLAY_FRAME_RATE_TYPE_VARIABLE = "variable"; + + private @NotNull String tag; + private int segmentId; + private long size; + private int duration; + private @NotNull String encoding = REPLAY_ENCODING; + private @NotNull String container = REPLAY_CONTAINER; + private int height; + private int width; + private int frameCount; + private @NotNull String frameRateType = REPLAY_FRAME_RATE_TYPE_CONSTANT; + private int frameRate; + private int left; + private int top; + // to support unknown json attributes with nesting, we have to have unknown map for each of the + // nested object in json: { ..., "data": { ..., "payload": { ... } } } + private @Nullable Map unknown; + private @Nullable Map payloadUnknown; + private @Nullable Map dataUnknown; + + public RRWebVideoEvent() { + super(RRWebEventType.Custom); + tag = EVENT_TAG; + } + + @NotNull + public String getTag() { + return tag; + } + + public void setTag(final @NotNull String tag) { + this.tag = tag; + } + + public int getSegmentId() { + return segmentId; + } + + public void setSegmentId(final int segmentId) { + this.segmentId = segmentId; + } + + public long getSize() { + return size; + } + + public void setSize(final long size) { + this.size = size; + } + + public int getDuration() { + return duration; + } + + public void setDuration(final int duration) { + this.duration = duration; + } + + @NotNull + public String getEncoding() { + return encoding; + } + + public void setEncoding(final @NotNull String encoding) { + this.encoding = encoding; + } + + @NotNull + public String getContainer() { + return container; + } + + public void setContainer(final @NotNull String container) { + this.container = container; + } + + public int getHeight() { + return height; + } + + public void setHeight(final int height) { + this.height = height; + } + + public int getWidth() { + return width; + } + + public void setWidth(final int width) { + this.width = width; + } + + public int getFrameCount() { + return frameCount; + } + + public void setFrameCount(final int frameCount) { + this.frameCount = frameCount; + } + + @NotNull + public String getFrameRateType() { + return frameRateType; + } + + public void setFrameRateType(final @NotNull String frameRateType) { + this.frameRateType = frameRateType; + } + + public int getFrameRate() { + return frameRate; + } + + public void setFrameRate(final int frameRate) { + this.frameRate = frameRate; + } + + public int getLeft() { + return left; + } + + public void setLeft(final int left) { + this.left = left; + } + + public int getTop() { + return top; + } + + public void setTop(final int top) { + this.top = top; + } + + public @Nullable Map getPayloadUnknown() { + return payloadUnknown; + } + + public void setPayloadUnknown(final @Nullable Map payloadUnknown) { + this.payloadUnknown = payloadUnknown; + } + + public @Nullable Map getDataUnknown() { + return dataUnknown; + } + + public void setDataUnknown(final @Nullable Map dataUnknown) { + this.dataUnknown = dataUnknown; + } + + @Override + public @Nullable Map getUnknown() { + return unknown; + } + + @Override + public void setUnknown(final @Nullable Map unknown) { + this.unknown = unknown; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + RRWebVideoEvent that = (RRWebVideoEvent) o; + return segmentId == that.segmentId + && size == that.size + && duration == that.duration + && height == that.height + && width == that.width + && frameCount == that.frameCount + && frameRate == that.frameRate + && left == that.left + && top == that.top + && Objects.equals(tag, that.tag) + && Objects.equals(encoding, that.encoding) + && Objects.equals(container, that.container) + && Objects.equals(frameRateType, that.frameRateType); + } + + @Override + public int hashCode() { + return Objects.hash( + super.hashCode(), + tag, + segmentId, + size, + duration, + encoding, + container, + height, + width, + frameCount, + frameRateType, + frameRate, + left, + top); + } + + // region json + + // rrweb uses camelCase hence the json keys are in camelCase here + public static final class JsonKeys { + public static final String DATA = "data"; + public static final String PAYLOAD = "payload"; + public static final String SEGMENT_ID = "segmentId"; + public static final String SIZE = "size"; + public static final String DURATION = "duration"; + public static final String ENCODING = "encoding"; + public static final String CONTAINER = "container"; + public static final String HEIGHT = "height"; + public static final String WIDTH = "width"; + public static final String FRAME_COUNT = "frameCount"; + public static final String FRAME_RATE_TYPE = "frameRateType"; + public static final String FRAME_RATE = "frameRate"; + public static final String LEFT = "left"; + public static final String TOP = "top"; + } + + @Override + public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { + writer.beginObject(); + new RRWebEvent.Serializer().serialize(this, writer, logger); + writer.name(JsonKeys.DATA); + serializeData(writer, logger); + if (unknown != null) { + for (final String key : unknown.keySet()) { + final Object value = unknown.get(key); + writer.name(key); + writer.value(logger, value); + } + } + writer.endObject(); + } + + private void serializeData(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { + writer.beginObject(); + writer.name(RRWebEvent.JsonKeys.TAG).value(tag); + writer.name(JsonKeys.PAYLOAD); + serializePayload(writer, logger); + if (dataUnknown != null) { + for (String key : dataUnknown.keySet()) { + Object value = dataUnknown.get(key); + writer.name(key); + writer.value(logger, value); + } + } + writer.endObject(); + } + + private void serializePayload(final @NotNull ObjectWriter writer, final @NotNull ILogger logger) + throws IOException { + writer.beginObject(); + writer.name(JsonKeys.SEGMENT_ID).value(segmentId); + writer.name(JsonKeys.SIZE).value(size); + writer.name(JsonKeys.DURATION).value(duration); + writer.name(JsonKeys.ENCODING).value(encoding); + writer.name(JsonKeys.CONTAINER).value(container); + writer.name(JsonKeys.HEIGHT).value(height); + writer.name(JsonKeys.WIDTH).value(width); + writer.name(JsonKeys.FRAME_COUNT).value(frameCount); + writer.name(JsonKeys.FRAME_RATE).value(frameRate); + writer.name(JsonKeys.FRAME_RATE_TYPE).value(frameRateType); + writer.name(JsonKeys.LEFT).value(left); + writer.name(JsonKeys.TOP).value(top); + if (payloadUnknown != null) { + for (final String key : payloadUnknown.keySet()) { + final Object value = payloadUnknown.get(key); + writer.name(key); + writer.value(logger, value); + } + } + writer.endObject(); + } + + public static final class Deserializer implements JsonDeserializer { + + @SuppressWarnings("unchecked") + @Override + public @NotNull RRWebVideoEvent deserialize( + final @NotNull ObjectReader reader, final @NotNull ILogger logger) throws Exception { + reader.beginObject(); + @Nullable Map unknown = null; + + final RRWebVideoEvent event = new RRWebVideoEvent(); + final RRWebEvent.Deserializer baseEventDeserializer = new RRWebEvent.Deserializer(); + + while (reader.peek() == JsonToken.NAME) { + final String nextName = reader.nextName(); + switch (nextName) { + case RRWebMetaEvent.JsonKeys.DATA: + deserializeData(event, reader, logger); + break; + default: + if (!baseEventDeserializer.deserializeValue(event, nextName, reader, logger)) { + if (unknown == null) { + unknown = new HashMap<>(); + } + reader.nextUnknown(logger, unknown, nextName); + } + break; + } + } + event.setUnknown(unknown); + reader.endObject(); + return event; + } + + private void deserializeData( + final @NotNull RRWebVideoEvent event, + final @NotNull ObjectReader reader, + final @NotNull ILogger logger) + throws Exception { + @Nullable Map dataUnknown = null; + + reader.beginObject(); + while (reader.peek() == JsonToken.NAME) { + final String nextName = reader.nextName(); + switch (nextName) { + case RRWebEvent.JsonKeys.TAG: + final String tag = reader.nextStringOrNull(); + event.tag = tag == null ? "" : tag; + break; + case JsonKeys.PAYLOAD: + deserializePayload(event, reader, logger); + break; + default: + if (dataUnknown == null) { + dataUnknown = new ConcurrentHashMap<>(); + } + reader.nextUnknown(logger, dataUnknown, nextName); + } + } + event.setDataUnknown(dataUnknown); + reader.endObject(); + } + + private void deserializePayload( + final @NotNull RRWebVideoEvent event, + final @NotNull ObjectReader reader, + final @NotNull ILogger logger) + throws Exception { + @Nullable Map payloadUnknown = null; + + reader.beginObject(); + while (reader.peek() == JsonToken.NAME) { + final String nextName = reader.nextName(); + switch (nextName) { + case JsonKeys.SEGMENT_ID: + event.segmentId = reader.nextInt(); + break; + case JsonKeys.SIZE: + final Long size = reader.nextLongOrNull(); + event.size = size == null ? 0 : size; + break; + case JsonKeys.DURATION: + event.duration = reader.nextInt(); + break; + case JsonKeys.CONTAINER: + final String container = reader.nextStringOrNull(); + event.container = container == null ? "" : container; + break; + case JsonKeys.ENCODING: + final String encoding = reader.nextStringOrNull(); + event.encoding = encoding == null ? "" : encoding; + break; + case JsonKeys.HEIGHT: + final Integer height = reader.nextIntegerOrNull(); + event.height = height == null ? 0 : height; + break; + case JsonKeys.WIDTH: + final Integer width = reader.nextIntegerOrNull(); + event.width = width == null ? 0 : width; + break; + case JsonKeys.FRAME_COUNT: + final Integer frameCount = reader.nextIntegerOrNull(); + event.frameCount = frameCount == null ? 0 : frameCount; + break; + case JsonKeys.FRAME_RATE: + final Integer frameRate = reader.nextIntegerOrNull(); + event.frameRate = frameRate == null ? 0 : frameRate; + break; + case JsonKeys.FRAME_RATE_TYPE: + final String frameRateType = reader.nextStringOrNull(); + event.frameRateType = frameRateType == null ? "" : frameRateType; + break; + case JsonKeys.LEFT: + final Integer left = reader.nextIntegerOrNull(); + event.left = left == null ? 0 : left; + break; + case JsonKeys.TOP: + final Integer top = reader.nextIntegerOrNull(); + event.top = top == null ? 0 : top; + break; + default: + if (payloadUnknown == null) { + payloadUnknown = new ConcurrentHashMap<>(); + } + reader.nextUnknown(logger, payloadUnknown, nextName); + } + } + event.setPayloadUnknown(payloadUnknown); + reader.endObject(); + } + } + // endregion json +} diff --git a/sentry/src/main/java/io/sentry/util/MapObjectReader.java b/sentry/src/main/java/io/sentry/util/MapObjectReader.java new file mode 100644 index 00000000000..6cb05989b25 --- /dev/null +++ b/sentry/src/main/java/io/sentry/util/MapObjectReader.java @@ -0,0 +1,356 @@ +package io.sentry.util; + +import io.sentry.ILogger; +import io.sentry.JsonDeserializer; +import io.sentry.ObjectReader; +import io.sentry.SentryLevel; +import io.sentry.vendor.gson.stream.JsonToken; +import java.io.IOException; +import java.util.AbstractMap; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Date; +import java.util.Deque; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@SuppressWarnings("unchecked") +public final class MapObjectReader implements ObjectReader { + + private final Deque> stack; + + public MapObjectReader(final Map root) { + stack = new ArrayDeque<>(); + stack.addLast(new AbstractMap.SimpleEntry<>(null, root)); + } + + @Override + public void nextUnknown( + final @NotNull ILogger logger, final Map unknown, final String name) { + try { + unknown.put(name, nextObjectOrNull()); + } catch (Exception exception) { + logger.log(SentryLevel.ERROR, exception, "Error deserializing unknown key: %s", name); + } + } + + @Nullable + @Override + public List nextListOrNull( + final @NotNull ILogger logger, final @NotNull JsonDeserializer deserializer) + throws IOException { + return nextValueOrNull(); + } + + @Nullable + @Override + public Map nextMapOrNull( + final @NotNull ILogger logger, final @NotNull JsonDeserializer deserializer) + throws IOException { + return nextValueOrNull(); + } + + @Nullable + @Override + public T nextOrNull( + final @NotNull ILogger logger, final @NotNull JsonDeserializer deserializer) + throws Exception { + return nextValueOrNull(logger, deserializer); + } + + @Nullable + @Override + public Date nextDateOrNull(final @NotNull ILogger logger) throws IOException { + final String dateString = nextStringOrNull(); + return ObjectReader.dateOrNull(dateString, logger); + } + + @Nullable + @Override + public TimeZone nextTimeZoneOrNull(final @NotNull ILogger logger) throws IOException { + final String timeZoneId = nextStringOrNull(); + return timeZoneId != null ? TimeZone.getTimeZone(timeZoneId) : null; + } + + @Nullable + @Override + public Object nextObjectOrNull() throws IOException { + return nextValueOrNull(); + } + + @NotNull + @Override + public JsonToken peek() throws IOException { + if (stack.isEmpty()) { + return JsonToken.END_DOCUMENT; + } + + final Map.Entry currentEntry = stack.peekLast(); + if (currentEntry == null) { + return JsonToken.END_DOCUMENT; + } + + if (currentEntry.getKey() != null) { + return JsonToken.NAME; + } + + final Object value = currentEntry.getValue(); + + if (value instanceof Map) { + return JsonToken.BEGIN_OBJECT; + } else if (value instanceof List) { + return JsonToken.BEGIN_ARRAY; + } else if (value instanceof String) { + return JsonToken.STRING; + } else if (value instanceof Number) { + return JsonToken.NUMBER; + } else if (value instanceof Boolean) { + return JsonToken.BOOLEAN; + } else if (value instanceof JsonToken) { + return (JsonToken) value; + } else { + return JsonToken.END_DOCUMENT; + } + } + + @NotNull + @Override + public String nextName() throws IOException { + final Map.Entry currentEntry = stack.peekLast(); + if (currentEntry != null && currentEntry.getKey() != null) { + return currentEntry.getKey(); + } + throw new IOException("Expected a name but was " + peek()); + } + + @Override + public void beginObject() throws IOException { + final Map.Entry currentEntry = stack.removeLast(); + if (currentEntry == null) { + throw new IOException("No more entries"); + } + final Object value = currentEntry.getValue(); + if (value instanceof Map) { + // insert a dummy entry to indicate end of an object + stack.addLast(new AbstractMap.SimpleEntry<>(null, JsonToken.END_OBJECT)); + // extract map entries onto the stack + for (Map.Entry entry : ((Map) value).entrySet()) { + stack.addLast(entry); + } + } else { + throw new IOException("Current token is not an object"); + } + } + + @Override + public void endObject() throws IOException { + if (stack.size() > 1) { + stack.removeLast(); // Pop the current map from stack + } + } + + @Override + public void beginArray() throws IOException { + final Map.Entry currentEntry = stack.removeLast(); + if (currentEntry == null) { + throw new IOException("No more entries"); + } + final Object value = currentEntry.getValue(); + if (value instanceof List) { + // insert a dummy entry to indicate end of an object + stack.addLast(new AbstractMap.SimpleEntry<>(null, JsonToken.END_ARRAY)); + // extract map entries onto the stack + for (int i = ((List) value).size() - 1; i >= 0; i--) { + final Object entry = ((List) value).get(i); + stack.addLast(new AbstractMap.SimpleEntry<>(null, entry)); + } + } else { + throw new IOException("Current token is not an object"); + } + } + + @Override + public void endArray() throws IOException { + if (stack.size() > 1) { + stack.removeLast(); // Pop the current array from stack + } + } + + @Override + public boolean hasNext() throws IOException { + return !stack.isEmpty(); + } + + @Override + public int nextInt() throws IOException { + final Object value = nextValueOrNull(); + if (value instanceof Number) { + return ((Number) value).intValue(); + } else { + throw new IOException("Expected int"); + } + } + + @Nullable + @Override + public Integer nextIntegerOrNull() throws IOException { + final Object value = nextValueOrNull(); + if (value instanceof Number) { + return ((Number) value).intValue(); + } + return null; + } + + @Override + public long nextLong() throws IOException { + final Object value = nextValueOrNull(); + if (value instanceof Number) { + return ((Number) value).longValue(); + } else { + throw new IOException("Expected long"); + } + } + + @Nullable + @Override + public Long nextLongOrNull() throws IOException { + final Object value = nextValueOrNull(); + if (value instanceof Number) { + return ((Number) value).longValue(); + } + return null; + } + + @Override + public String nextString() throws IOException { + final String value = nextValueOrNull(); + if (value != null) { + return value; + } else { + throw new IOException("Expected string"); + } + } + + @Nullable + @Override + public String nextStringOrNull() throws IOException { + return nextValueOrNull(); + } + + @Override + public boolean nextBoolean() throws IOException { + final Boolean value = nextValueOrNull(); + if (value != null) { + return value; + } else { + throw new IOException("Expected boolean"); + } + } + + @Nullable + @Override + public Boolean nextBooleanOrNull() throws IOException { + return nextValueOrNull(); + } + + @Override + public double nextDouble() throws IOException { + final Object value = nextValueOrNull(); + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } else { + throw new IOException("Expected double"); + } + } + + @Nullable + @Override + public Double nextDoubleOrNull() throws IOException { + final Object value = nextValueOrNull(); + if (value instanceof Number) { + return ((Number) value).doubleValue(); + } + return null; + } + + @Nullable + @Override + public Float nextFloatOrNull() throws IOException { + final Object value = nextValueOrNull(); + if (value instanceof Number) { + return ((Number) value).floatValue(); + } + return null; + } + + @Override + public float nextFloat() throws IOException { + final Object value = nextValueOrNull(); + if (value instanceof Number) { + return ((Number) value).floatValue(); + } else { + throw new IOException("Expected float"); + } + } + + @Override + public void nextNull() throws IOException { + final Object value = nextValueOrNull(); + if (value != null) { + throw new IOException("Expected null but was " + peek()); + } + } + + @Override + public void setLenient(final boolean lenient) {} + + @Override + public void skipValue() throws IOException {} + + @SuppressWarnings("TypeParameterUnusedInFormals") + @Nullable + private T nextValueOrNull() throws IOException { + try { + return nextValueOrNull(null, null); + } catch (Exception e) { + throw new IOException(e); + } + } + + @SuppressWarnings("TypeParameterUnusedInFormals") + @Nullable + private T nextValueOrNull( + final @Nullable ILogger logger, final @Nullable JsonDeserializer deserializer) + throws Exception { + final Map.Entry currentEntry = stack.peekLast(); + if (currentEntry == null) { + return null; + } + final T value = (T) currentEntry.getValue(); + if (deserializer != null && logger != null) { + return deserializer.deserialize(this, logger); + } else if (value instanceof List) { + List list = new ArrayList<>((List) value); + if (!list.isEmpty()) { + final T next = (T) list.remove(0); + if (next instanceof Map) { + stack.addLast(new AbstractMap.SimpleEntry<>(null, next)); + } + return next; + } + } else if (value instanceof Map) { + stack.addLast(new AbstractMap.SimpleEntry<>(null, value)); + return value; + } + stack.removeLast(); + return value; + } + + @Override + public void close() throws IOException { + stack.clear(); + } +} diff --git a/sentry/src/main/java/io/sentry/util/MapObjectWriter.java b/sentry/src/main/java/io/sentry/util/MapObjectWriter.java index 26f80eddc29..0bbc70a779d 100644 --- a/sentry/src/main/java/io/sentry/util/MapObjectWriter.java +++ b/sentry/src/main/java/io/sentry/util/MapObjectWriter.java @@ -120,6 +120,11 @@ public MapObjectWriter value(final @NotNull ILogger logger, final @Nullable Obje return this; } + @Override + public void setLenient(boolean lenient) { + // no-op + } + @Override public MapObjectWriter beginArray() throws IOException { stack.add(new ArrayList<>()); @@ -151,6 +156,12 @@ public MapObjectWriter value(final @Nullable String value) throws IOException { return this; } + @Override + public ObjectWriter jsonValue(@Nullable String value) throws IOException { + // no-op + return this; + } + @Override public MapObjectWriter nullValue() throws IOException { postValue((Object) null); diff --git a/sentry/src/test/java/io/sentry/JsonObjectReaderTest.kt b/sentry/src/test/java/io/sentry/JsonObjectReaderTest.kt index 976c88a1cd0..ec18b4ed5c2 100644 --- a/sentry/src/test/java/io/sentry/JsonObjectReaderTest.kt +++ b/sentry/src/test/java/io/sentry/JsonObjectReaderTest.kt @@ -285,7 +285,7 @@ class JsonObjectReaderTest { var bar: String? = null ) { class Deserializer : JsonDeserializer { - override fun deserialize(reader: JsonObjectReader, logger: ILogger): Deserializable { + override fun deserialize(reader: ObjectReader, logger: ILogger): Deserializable { return Deserializable().apply { reader.beginObject() reader.nextName() diff --git a/sentry/src/test/java/io/sentry/SentryClientTest.kt b/sentry/src/test/java/io/sentry/SentryClientTest.kt index 8fab30790f2..0733e6ea45f 100644 --- a/sentry/src/test/java/io/sentry/SentryClientTest.kt +++ b/sentry/src/test/java/io/sentry/SentryClientTest.kt @@ -5,7 +5,6 @@ import io.sentry.Session.State.Crashed import io.sentry.clientreport.ClientReportTestHelper.Companion.assertClientReport import io.sentry.clientreport.DiscardReason import io.sentry.clientreport.DiscardedEvent -import io.sentry.clientreport.DropEverythingEventProcessor import io.sentry.exception.SentryEnvelopeException import io.sentry.hints.AbnormalExit import io.sentry.hints.ApplyScopeData @@ -41,6 +40,7 @@ import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever +import org.msgpack.core.MessagePack import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.File @@ -2480,6 +2480,105 @@ class SentryClientTest { ) } + @Test + fun `when captureReplayEvent, envelope is sent`() { + val sut = fixture.getSut() + val replayEvent = createReplayEvent() + + sut.captureReplayEvent(replayEvent, null, null) + + verify(fixture.transport).send( + check { actual -> + assertEquals(replayEvent.eventId, actual.header.eventId) + assertEquals(fixture.sentryOptions.sdkVersion, actual.header.sdkVersion) + + assertEquals(1, actual.items.count()) + val item = actual.items.first() + assertEquals(SentryItemType.ReplayVideo, item.header.type) + + val unpacker = MessagePack.newDefaultUnpacker(item.data) + val mapSize = unpacker.unpackMapHeader() + assertEquals(1, mapSize) + }, + any() + ) + } + + @Test + fun `when captureReplayEvent with recording, adds it to payload`() { + val sut = fixture.getSut() + val replayEvent = createReplayEvent() + + val hint = Hint().apply { replayRecording = createReplayRecording() } + sut.captureReplayEvent(replayEvent, null, hint) + + verify(fixture.transport).send( + check { actual -> + assertEquals(replayEvent.eventId, actual.header.eventId) + assertEquals(fixture.sentryOptions.sdkVersion, actual.header.sdkVersion) + + assertEquals(1, actual.items.count()) + val item = actual.items.first() + assertEquals(SentryItemType.ReplayVideo, item.header.type) + + val unpacker = MessagePack.newDefaultUnpacker(item.data) + val mapSize = unpacker.unpackMapHeader() + assertEquals(2, mapSize) + }, + any() + ) + } + + @Test + fun `when captureReplayEvent, omits breadcrumbs and extras from scope`() { + val sut = fixture.getSut() + val replayEvent = createReplayEvent() + + sut.captureReplayEvent(replayEvent, createScope(), null) + + verify(fixture.transport).send( + check { actual -> + val item = actual.items.first() + + val unpacker = MessagePack.newDefaultUnpacker(item.data) + val mapSize = unpacker.unpackMapHeader() + for (i in 0 until mapSize) { + val key = unpacker.unpackString() + when (key) { + SentryItemType.ReplayEvent.itemType -> { + val replayEventLength = unpacker.unpackBinaryHeader() + val replayEventBytes = unpacker.readPayload(replayEventLength) + val actualReplayEvent = fixture.sentryOptions.serializer.deserialize( + InputStreamReader(replayEventBytes.inputStream()), + SentryReplayEvent::class.java + ) + // sanity check + assertEquals("id", actualReplayEvent!!.user!!.id) + + assertNull(actualReplayEvent.breadcrumbs) + assertNull(actualReplayEvent.extras) + } + } + } + }, + any() + ) + } + + @Test + fun `when replay event is dropped, captures client report with datacategory replay`() { + fixture.sentryOptions.addEventProcessor(DropEverythingEventProcessor()) + val sut = fixture.getSut() + val replayEvent = createReplayEvent() + + sut.captureReplayEvent(replayEvent, createScope(), null) + + assertClientReport( + fixture.sentryOptions.clientReportRecorder, + listOf(DiscardedEvent(DiscardReason.EVENT_PROCESSOR.reason, DataCategory.Replay.category, 1)) + ) + } + private fun givenScopeWithStartedSession(errored: Boolean = false, crashed: Boolean = false): IScope { val scope = createScope(fixture.sentryOptions) scope.startSession() @@ -2538,6 +2637,21 @@ class SentryClientTest { } } + private fun createReplayEvent(): SentryReplayEvent = SentryReplayEvent().apply { + replayId = SentryId("f715e1d64ef64ea3ad7744b5230813c3") + segmentId = 0 + timestamp = DateUtils.getDateTimeWithMillisPrecision("987654321.123") + replayStartTimestamp = DateUtils.getDateTimeWithMillisPrecision("987654321.123") + urls = listOf("ScreenOne") + errorIds = listOf("ab3a347a4cc14fd4b4cf1dc56b670c5b") + traceIds = listOf("340cfef948204549ac07c3b353c81c50") + } + + private fun createReplayRecording(): ReplayRecording = ReplayRecording().apply { + segmentId = 0 + payload = emptyList() + } + private fun createScope(options: SentryOptions = SentryOptions()): IScope { return Scope(options).apply { addBreadcrumb( @@ -2721,4 +2835,8 @@ class DropEverythingEventProcessor : EventProcessor { ): SentryTransaction? { return null } + + override fun process(event: SentryReplayEvent, hint: Hint): SentryReplayEvent? { + return null + } } diff --git a/sentry/src/test/java/io/sentry/SentryEnvelopeItemTest.kt b/sentry/src/test/java/io/sentry/SentryEnvelopeItemTest.kt index 98178976510..efc5e5cadfe 100644 --- a/sentry/src/test/java/io/sentry/SentryEnvelopeItemTest.kt +++ b/sentry/src/test/java/io/sentry/SentryEnvelopeItemTest.kt @@ -1,6 +1,8 @@ package io.sentry import io.sentry.exception.SentryEnvelopeException +import io.sentry.protocol.ReplayRecordingSerializationTest +import io.sentry.protocol.SentryReplayEventSerializationTest import io.sentry.protocol.User import io.sentry.protocol.ViewHierarchy import io.sentry.test.injectForField @@ -10,12 +12,15 @@ import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever +import org.msgpack.core.MessagePack import java.io.BufferedWriter import java.io.ByteArrayOutputStream import java.io.File import java.io.IOException +import java.io.InputStreamReader import java.io.OutputStreamWriter import java.nio.charset.Charset +import java.nio.file.Files import kotlin.test.AfterTest import kotlin.test.Test import kotlin.test.assertEquals @@ -66,7 +71,12 @@ class SentryEnvelopeItemTest { fun `fromAttachment with bytes`() { val attachment = Attachment(fixture.bytesAllowed, fixture.filename) - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertAttachment(attachment, fixture.bytesAllowed, item) } @@ -78,7 +88,12 @@ class SentryEnvelopeItemTest { val attachment = Attachment(viewHierarchy, fixture.filename, "text/plain", null, false) - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertAttachment(attachment, viewHierarchySerialized, item) } @@ -87,7 +102,12 @@ class SentryEnvelopeItemTest { fun `fromAttachment with attachmentType`() { val attachment = Attachment(fixture.pathname, fixture.filename, "", true, "event.minidump") - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertEquals("event.minidump", item.header.attachmentType) } @@ -98,7 +118,12 @@ class SentryEnvelopeItemTest { file.writeBytes(fixture.bytesAllowed) val attachment = Attachment(file.path) - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertAttachment(attachment, fixture.bytesAllowed, item) } @@ -110,7 +135,12 @@ class SentryEnvelopeItemTest { file.writeBytes(twoMB) val attachment = Attachment(file.absolutePath) - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertAttachment(attachment, twoMB, item) } @@ -119,7 +149,12 @@ class SentryEnvelopeItemTest { fun `fromAttachment with non existent file`() { val attachment = Attachment("I don't exist", "file.txt") - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertFailsWith( "Reading the attachment ${attachment.pathname} failed, because the file located at " + @@ -139,7 +174,12 @@ class SentryEnvelopeItemTest { if (changedFileReadPermission) { val attachment = Attachment(file.path, "file.txt") - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertFailsWith( "Reading the attachment ${attachment.pathname} failed, " + @@ -162,7 +202,12 @@ class SentryEnvelopeItemTest { val securityManager = DenyReadFileSecurityManager(fixture.pathname) System.setSecurityManager(securityManager) - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertFailsWith("Reading the attachment ${attachment.pathname} failed.") { item.data @@ -181,7 +226,12 @@ class SentryEnvelopeItemTest { // reflection instead. attachment.injectForField("pathname", null) - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertFailsWith( "Couldn't attach the attachment ${attachment.filename}.\n" + @@ -196,7 +246,12 @@ class SentryEnvelopeItemTest { val image = this::class.java.classLoader.getResource("Tongariro.jpg")!! val attachment = Attachment(image.path) - val item = SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertAttachment(attachment, image.readBytes(), item) } @@ -204,7 +259,12 @@ class SentryEnvelopeItemTest { fun `fromAttachment with bytes too big`() { val attachment = Attachment(fixture.bytesTooBig, fixture.filename) val exception = assertFailsWith { - SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize).data + SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ).data } assertEquals( @@ -227,7 +287,12 @@ class SentryEnvelopeItemTest { val attachment = Attachment(serializable, fixture.filename, "text/plain", null, false) val exception = assertFailsWith { - SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize).data + SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ).data } assertEquals( @@ -246,7 +311,12 @@ class SentryEnvelopeItemTest { val attachment = Attachment(file.path) val exception = assertFailsWith { - SentryEnvelopeItem.fromAttachment(fixture.serializer, fixture.options.logger, attachment, fixture.maxAttachmentSize).data + SentryEnvelopeItem.fromAttachment( + fixture.serializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ).data } assertEquals( @@ -261,7 +331,12 @@ class SentryEnvelopeItemTest { fun `fromAttachment with bytesFrom serializable are null`() { val attachment = Attachment(mock(), "mock-file-name", null, null, false) - val item = SentryEnvelopeItem.fromAttachment(fixture.errorSerializer, fixture.options.logger, attachment, fixture.maxAttachmentSize) + val item = SentryEnvelopeItem.fromAttachment( + fixture.errorSerializer, + fixture.options.logger, + attachment, + fixture.maxAttachmentSize + ) assertFailsWith( "Couldn't attach the attachment ${attachment.filename}.\n" + @@ -279,8 +354,13 @@ class SentryEnvelopeItemTest { } file.writeBytes(fixture.bytes) - SentryEnvelopeItem.fromProfilingTrace(profilingTraceData, fixture.maxAttachmentSize, fixture.serializer).data - verify(profilingTraceData).sampledProfile = Base64.encodeToString(fixture.bytes, Base64.NO_WRAP or Base64.NO_PADDING) + SentryEnvelopeItem.fromProfilingTrace( + profilingTraceData, + fixture.maxAttachmentSize, + fixture.serializer + ).data + verify(profilingTraceData).sampledProfile = + Base64.encodeToString(fixture.bytes, Base64.NO_WRAP or Base64.NO_PADDING) } @Test @@ -292,7 +372,11 @@ class SentryEnvelopeItemTest { file.writeBytes(fixture.bytes) assert(file.exists()) - val traceData = SentryEnvelopeItem.fromProfilingTrace(profilingTraceData, fixture.maxAttachmentSize, mock()) + val traceData = SentryEnvelopeItem.fromProfilingTrace( + profilingTraceData, + fixture.maxAttachmentSize, + mock() + ) assert(file.exists()) traceData.data assertFalse(file.exists()) @@ -306,7 +390,11 @@ class SentryEnvelopeItemTest { } assertFailsWith("Dropping profiling trace data, because the file ${file.path} doesn't exists") { - SentryEnvelopeItem.fromProfilingTrace(profilingTraceData, fixture.maxAttachmentSize, mock()).data + SentryEnvelopeItem.fromProfilingTrace( + profilingTraceData, + fixture.maxAttachmentSize, + mock() + ).data } } @@ -319,7 +407,11 @@ class SentryEnvelopeItemTest { file.writeBytes(fixture.bytes) file.setReadable(false) assertFailsWith("Dropping profiling trace data, because the file ${file.path} doesn't exists") { - SentryEnvelopeItem.fromProfilingTrace(profilingTraceData, fixture.maxAttachmentSize, mock()).data + SentryEnvelopeItem.fromProfilingTrace( + profilingTraceData, + fixture.maxAttachmentSize, + mock() + ).data } } @@ -331,7 +423,11 @@ class SentryEnvelopeItemTest { whenever(it.traceFile).thenReturn(file) } - val traceData = SentryEnvelopeItem.fromProfilingTrace(profilingTraceData, fixture.maxAttachmentSize, mock()) + val traceData = SentryEnvelopeItem.fromProfilingTrace( + profilingTraceData, + fixture.maxAttachmentSize, + mock() + ) assertFailsWith("Profiling trace file is empty") { traceData.data } @@ -346,7 +442,11 @@ class SentryEnvelopeItemTest { } val exception = assertFailsWith { - SentryEnvelopeItem.fromProfilingTrace(profilingTraceData, fixture.maxAttachmentSize, mock()).data + SentryEnvelopeItem.fromProfilingTrace( + profilingTraceData, + fixture.maxAttachmentSize, + mock() + ).data } assertEquals( @@ -357,6 +457,58 @@ class SentryEnvelopeItemTest { ) } + @Test + fun `fromReplay encodes payload into msgpack`() { + val file = Files.createTempFile("replay", "").toFile() + val videoBytes = + this::class.java.classLoader.getResource("Tongariro.jpg")!!.readBytes() + file.writeBytes(videoBytes) + + val replayEvent = SentryReplayEventSerializationTest.Fixture().getSut().apply { + videoFile = file + } + val replayRecording = ReplayRecordingSerializationTest.Fixture().getSut() + val replayItem = SentryEnvelopeItem + .fromReplay(fixture.serializer, fixture.options.logger, replayEvent, replayRecording) + + assertEquals(SentryItemType.ReplayVideo, replayItem.header.type) + + assertPayload(replayItem, replayEvent, replayRecording, videoBytes) + } + + @Test + fun `fromReplay does not add video item when no bytes`() { + val file = File(fixture.pathname) + file.writeBytes(ByteArray(0)) + + val replayEvent = SentryReplayEventSerializationTest.Fixture().getSut().apply { + videoFile = file + } + + val replayItem = SentryEnvelopeItem + .fromReplay(fixture.serializer, fixture.options.logger, replayEvent, null) + replayItem.data + assertPayload(replayItem, replayEvent, null, ByteArray(0)) { mapSize -> + assertEquals(1, mapSize) + } + } + + @Test + fun `fromReplay deletes file only after reading data`() { + val file = File(fixture.pathname) + val replayEvent = SentryReplayEventSerializationTest.Fixture().getSut().apply { + videoFile = file + } + + file.writeBytes(fixture.bytes) + assert(file.exists()) + val replayItem = SentryEnvelopeItem + .fromReplay(fixture.serializer, fixture.options.logger, replayEvent, null) + assert(file.exists()) + replayItem.data + assertFalse(file.exists()) + } + private fun createSession(): Session { return Session("dis", User(), "env", "rel") } @@ -379,4 +531,45 @@ class SentryEnvelopeItemTest { } } } + + private fun assertPayload( + replayItem: SentryEnvelopeItem, + replayEvent: SentryReplayEvent, + replayRecording: ReplayRecording?, + videoBytes: ByteArray, + mapSizeAsserter: (mapSize: Int) -> Unit = {} + ) { + val unpacker = MessagePack.newDefaultUnpacker(replayItem.data) + val mapSize = unpacker.unpackMapHeader() + mapSizeAsserter(mapSize) + for (i in 0 until mapSize) { + val key = unpacker.unpackString() + when (key) { + SentryItemType.ReplayEvent.itemType -> { + val replayEventLength = unpacker.unpackBinaryHeader() + val replayEventBytes = unpacker.readPayload(replayEventLength) + val actualReplayEvent = fixture.serializer.deserialize( + InputStreamReader(replayEventBytes.inputStream()), + SentryReplayEvent::class.java + ) + assertEquals(replayEvent, actualReplayEvent) + } + SentryItemType.ReplayRecording.itemType -> { + val replayRecordingLength = unpacker.unpackBinaryHeader() + val replayRecordingBytes = unpacker.readPayload(replayRecordingLength) + val actualReplayRecording = fixture.serializer.deserialize( + InputStreamReader(replayRecordingBytes.inputStream()), + ReplayRecording::class.java + ) + assertEquals(replayRecording, actualReplayRecording) + } + SentryItemType.ReplayVideo.itemType -> { + val videoLength = unpacker.unpackBinaryHeader() + val actualBytes = unpacker.readPayload(videoLength) + assertArrayEquals(videoBytes, actualBytes) + } + } + } + unpacker.close() + } } diff --git a/sentry/src/test/java/io/sentry/protocol/ReplayRecordingSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/ReplayRecordingSerializationTest.kt new file mode 100644 index 00000000000..d8f93ddfb5d --- /dev/null +++ b/sentry/src/test/java/io/sentry/protocol/ReplayRecordingSerializationTest.kt @@ -0,0 +1,45 @@ +package io.sentry.protocol + +import io.sentry.FileFromResources +import io.sentry.ILogger +import io.sentry.ReplayRecording +import io.sentry.protocol.SerializationUtils.deserializeJson +import io.sentry.protocol.SerializationUtils.serializeToString +import io.sentry.rrweb.RRWebMetaEventSerializationTest +import io.sentry.rrweb.RRWebVideoEventSerializationTest +import org.junit.Test +import org.mockito.kotlin.mock +import kotlin.test.assertEquals + +class ReplayRecordingSerializationTest { + class Fixture { + val logger = mock() + + fun getSut() = ReplayRecording().apply { + segmentId = 0 + payload = listOf( + RRWebMetaEventSerializationTest.Fixture().getSut(), + RRWebVideoEventSerializationTest.Fixture().getSut() + ) + } + } + + private val fixture = Fixture() + + @Test + fun serialize() { + val expected = FileFromResources.invoke("json/replay_recording.json") + .substringBeforeLast("\n") + val actual = serializeToString(fixture.getSut(), fixture.logger) + assertEquals(expected, actual) + } + + @Test + fun deserialize() { + val expectedJson = FileFromResources.invoke("json/replay_recording.json") + .substringBeforeLast("\n") + val actual = deserializeJson(expectedJson, ReplayRecording.Deserializer(), fixture.logger) + val actualJson = serializeToString(actual, fixture.logger) + assertEquals(expectedJson, actualJson) + } +} diff --git a/sentry/src/test/java/io/sentry/protocol/SentryBaseEventSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryBaseEventSerializationTest.kt index 4bc13559dae..3da517ef56f 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryBaseEventSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryBaseEventSerializationTest.kt @@ -2,8 +2,8 @@ package io.sentry.protocol import io.sentry.ILogger import io.sentry.JsonDeserializer -import io.sentry.JsonObjectReader import io.sentry.JsonSerializable +import io.sentry.ObjectReader import io.sentry.ObjectWriter import io.sentry.SentryBaseEvent import io.sentry.SentryIntegrationPackageStorage @@ -27,7 +27,7 @@ class SentryBaseEventSerializationTest { } class Deserializer : JsonDeserializer { - override fun deserialize(reader: JsonObjectReader, logger: ILogger): Sut { + override fun deserialize(reader: ObjectReader, logger: ILogger): Sut { val sut = Sut() reader.beginObject() diff --git a/sentry/src/test/java/io/sentry/protocol/SentryReplayEventSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryReplayEventSerializationTest.kt new file mode 100644 index 00000000000..6ecd6800767 --- /dev/null +++ b/sentry/src/test/java/io/sentry/protocol/SentryReplayEventSerializationTest.kt @@ -0,0 +1,62 @@ +package io.sentry.protocol + +import io.sentry.DateUtils +import io.sentry.ILogger +import io.sentry.SentryIntegrationPackageStorage +import io.sentry.SentryReplayEvent +import io.sentry.protocol.SerializationUtils.deserializeJson +import io.sentry.protocol.SerializationUtils.sanitizedFile +import io.sentry.protocol.SerializationUtils.serializeToString +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockito.kotlin.mock +import kotlin.test.assertEquals + +class SentryReplayEventSerializationTest { + + class Fixture { + val logger = mock() + + fun getSut() = SentryReplayEvent().apply { + replayId = SentryId("f715e1d64ef64ea3ad7744b5230813c3") + segmentId = 0 + timestamp = DateUtils.getDateTime("1942-07-09T12:55:34.000Z") + replayStartTimestamp = DateUtils.getDateTime("1942-07-09T12:55:34.000Z") + urls = listOf("ScreenOne") + errorIds = listOf("ab3a347a4cc14fd4b4cf1dc56b670c5b") + traceIds = listOf("340cfef948204549ac07c3b353c81c50") + SentryBaseEventSerializationTest.Fixture().update(this) + // irrelevant for replay + serverName = null + breadcrumbs = null + extras = null + } + } + private val fixture = Fixture() + + @Before + fun setup() { + SentryIntegrationPackageStorage.getInstance().clearStorage() + } + + @After + fun teardown() { + SentryIntegrationPackageStorage.getInstance().clearStorage() + } + + @Test + fun serialize() { + val expected = sanitizedFile("json/sentry_replay_event.json") + val actual = serializeToString(fixture.getSut(), fixture.logger) + assertEquals(expected, actual) + } + + @Test + fun deserialize() { + val expectedJson = sanitizedFile("json/sentry_replay_event.json") + val actual = deserializeJson(expectedJson, SentryReplayEvent.Deserializer(), fixture.logger) + val actualJson = serializeToString(actual, fixture.logger) + assertEquals(expectedJson, actualJson) + } +} diff --git a/sentry/src/test/java/io/sentry/rrweb/RRWebEventSerializationTest.kt b/sentry/src/test/java/io/sentry/rrweb/RRWebEventSerializationTest.kt new file mode 100644 index 00000000000..2c2b60cd28d --- /dev/null +++ b/sentry/src/test/java/io/sentry/rrweb/RRWebEventSerializationTest.kt @@ -0,0 +1,78 @@ +package io.sentry.rrweb + +import io.sentry.ILogger +import io.sentry.JsonDeserializer +import io.sentry.JsonSerializable +import io.sentry.ObjectReader +import io.sentry.ObjectWriter +import io.sentry.protocol.SerializationUtils.deserializeJson +import io.sentry.protocol.SerializationUtils.sanitizedFile +import io.sentry.protocol.SerializationUtils.serializeToString +import io.sentry.rrweb.RRWebEventType.Custom +import io.sentry.vendor.gson.stream.JsonToken +import org.junit.Test +import org.mockito.kotlin.mock +import kotlin.test.assertEquals + +class RRWebEventSerializationTest { + + /** + * Make subclass, as `RRWebEvent` initializers are protected. + */ + class Sut : RRWebEvent(), JsonSerializable { + override fun serialize(writer: ObjectWriter, logger: ILogger) { + writer.beginObject() + Serializer().serialize(this, writer, logger) + writer.endObject() + } + + class Deserializer : JsonDeserializer { + override fun deserialize(reader: ObjectReader, logger: ILogger): Sut { + val sut = Sut() + reader.beginObject() + + val baseEventDeserializer = RRWebEvent.Deserializer() + do { + val nextName = reader.nextName() + baseEventDeserializer.deserializeValue(sut, nextName, reader, logger) + } while (reader.hasNext() && reader.peek() == JsonToken.NAME) + reader.endObject() + return sut + } + } + } + + class Fixture { + val logger = mock() + + fun update(rrWebEvent: RRWebEvent) { + rrWebEvent.apply { + type = Custom + timestamp = 9999999 + } + } + } + private val fixture = Fixture() + + @Test + fun serialize() { + val expected = sanitizedFile("json/rrweb_event.json") + val sut = Sut().apply { fixture.update(this) } + val actual = serializeToString(sut, fixture.logger) + + assertEquals(expected, actual) + } + + @Test + fun deserialize() { + val expectedJson = sanitizedFile("json/rrweb_event.json") + val actual = deserializeJson( + expectedJson, + Sut.Deserializer(), + fixture.logger + ) + val actualJson = serializeToString(actual, fixture.logger) + + assertEquals(expectedJson, actualJson) + } +} diff --git a/sentry/src/test/java/io/sentry/rrweb/RRWebMetaEventSerializationTest.kt b/sentry/src/test/java/io/sentry/rrweb/RRWebMetaEventSerializationTest.kt new file mode 100644 index 00000000000..29ec354333e --- /dev/null +++ b/sentry/src/test/java/io/sentry/rrweb/RRWebMetaEventSerializationTest.kt @@ -0,0 +1,42 @@ +package io.sentry.rrweb + +import io.sentry.ILogger +import io.sentry.protocol.SerializationUtils.deserializeJson +import io.sentry.protocol.SerializationUtils.sanitizedFile +import io.sentry.protocol.SerializationUtils.serializeToString +import io.sentry.rrweb.RRWebEventType.Meta +import org.junit.Test +import org.mockito.kotlin.mock +import kotlin.test.assertEquals + +class RRWebMetaEventSerializationTest { + + class Fixture { + val logger = mock() + + fun getSut() = RRWebMetaEvent().apply { + href = "https://sentry.io" + height = 1920 + width = 1080 + type = Meta + timestamp = 1234567890 + } + } + + private val fixture = Fixture() + + @Test + fun serialize() { + val expected = sanitizedFile("json/rrweb_meta_event.json") + val actual = serializeToString(fixture.getSut(), fixture.logger) + assertEquals(expected, actual) + } + + @Test + fun deserialize() { + val expectedJson = sanitizedFile("json/rrweb_meta_event.json") + val actual = deserializeJson(expectedJson, RRWebMetaEvent.Deserializer(), fixture.logger) + val actualJson = serializeToString(actual, fixture.logger) + assertEquals(expectedJson, actualJson) + } +} diff --git a/sentry/src/test/java/io/sentry/rrweb/RRWebVideoEventSerializationTest.kt b/sentry/src/test/java/io/sentry/rrweb/RRWebVideoEventSerializationTest.kt new file mode 100644 index 00000000000..79bfd024564 --- /dev/null +++ b/sentry/src/test/java/io/sentry/rrweb/RRWebVideoEventSerializationTest.kt @@ -0,0 +1,47 @@ +package io.sentry.rrweb + +import io.sentry.ILogger +import io.sentry.protocol.SerializationUtils +import io.sentry.rrweb.RRWebEventType.Custom +import org.junit.Test +import org.mockito.kotlin.mock +import kotlin.test.assertEquals + +class RRWebVideoEventSerializationTest { + class Fixture { + val logger = mock() + + fun getSut() = RRWebVideoEvent().apply { + type = Custom + timestamp = 12345678901 + tag = "video" + segmentId = 0 + size = 4_000_000L + duration = 5000 + height = 1920 + width = 1080 + frameCount = 5 + frameRate = 1 + left = 100 + top = 100 + } + } + + private val fixture = Fixture() + + @Test + fun serialize() { + val expected = SerializationUtils.sanitizedFile("json/rrweb_video_event.json") + val actual = SerializationUtils.serializeToString(fixture.getSut(), fixture.logger) + assertEquals(expected, actual) + } + + @Test + fun deserialize() { + val expectedJson = SerializationUtils.sanitizedFile("json/rrweb_video_event.json") + val actual = + SerializationUtils.deserializeJson(expectedJson, RRWebVideoEvent.Deserializer(), fixture.logger) + val actualJson = SerializationUtils.serializeToString(actual, fixture.logger) + assertEquals(expectedJson, actualJson) + } +} diff --git a/sentry/src/test/java/io/sentry/util/MapObjectReaderTest.kt b/sentry/src/test/java/io/sentry/util/MapObjectReaderTest.kt new file mode 100644 index 00000000000..ab52919c438 --- /dev/null +++ b/sentry/src/test/java/io/sentry/util/MapObjectReaderTest.kt @@ -0,0 +1,131 @@ +package io.sentry.util + +import io.sentry.ILogger +import io.sentry.JsonDeserializer +import io.sentry.JsonSerializable +import io.sentry.NoOpLogger +import io.sentry.ObjectReader +import io.sentry.ObjectWriter +import io.sentry.vendor.gson.stream.JsonToken +import java.math.BigDecimal +import java.net.URI +import java.util.Currency +import java.util.Date +import java.util.Locale +import java.util.TimeZone +import java.util.UUID +import kotlin.test.Test +import kotlin.test.assertEquals + +class MapObjectReaderTest { + + enum class BasicEnum { + A + } + + data class BasicSerializable(var test: String = "string") : JsonSerializable { + + override fun serialize(writer: ObjectWriter, logger: ILogger) { + writer.beginObject() + .name("test") + .value(test) + .endObject() + } + + class Deserializer : JsonDeserializer { + override fun deserialize(reader: ObjectReader, logger: ILogger): BasicSerializable { + val basicSerializable = BasicSerializable() + reader.beginObject() + if (reader.nextName() == "test") { + basicSerializable.test = reader.nextString() + } + reader.endObject() + return basicSerializable + } + } + } + + @Test + fun `deserializes data correctly`() { + val logger = NoOpLogger.getInstance() + val data = mutableMapOf() + val writer = MapObjectWriter(data) + + writer.name("null").nullValue() + writer.name("int").value(1) + writer.name("boolean").value(true) + writer.name("long").value(Long.MAX_VALUE) + writer.name("double").value(Double.MAX_VALUE) + writer.name("number").value(BigDecimal(123)) + writer.name("date").value(logger, Date(0)) + writer.name("string").value("string") + + writer.name("TimeZone").value(logger, TimeZone.getTimeZone("Vienna")) + writer.name("JsonSerializable").value( + logger, + BasicSerializable() + ) + writer.name("Collection").value(logger, listOf("a", "b")) + writer.name("Arrays").value(logger, arrayOf("b", "c")) + writer.name("Map").value(logger, mapOf(kotlin.Pair("key", "value"))) + writer.name("Locale").value(logger, Locale.US) + writer.name("URI").value(logger, URI.create("http://www.example.com")) + writer.name("UUID").value(logger, UUID.fromString("00000000-1111-2222-3333-444444444444")) + writer.name("Currency").value(logger, Currency.getInstance("EUR")) + writer.name("Enum").value(logger, MapObjectWriterTest.BasicEnum.A) + + val reader = MapObjectReader(data) + reader.beginObject() + assertEquals(JsonToken.NAME, reader.peek()) + assertEquals("Enum", reader.nextName()) + assertEquals(BasicEnum.A, BasicEnum.valueOf(reader.nextString())) + assertEquals("Currency", reader.nextName()) + assertEquals(Currency.getInstance("EUR"), Currency.getInstance(reader.nextString())) + assertEquals("UUID", reader.nextName()) + assertEquals( + UUID.fromString("00000000-1111-2222-3333-444444444444"), + UUID.fromString(reader.nextString()) + ) + assertEquals("URI", reader.nextName()) + assertEquals(URI.create("http://www.example.com"), URI.create(reader.nextString())) + assertEquals("Locale", reader.nextName()) + assertEquals(Locale.US.toString(), reader.nextString()) + assertEquals("Map", reader.nextName()) + // nested object + reader.beginObject() + assertEquals("key", reader.nextName()) + assertEquals("value", reader.nextStringOrNull()) + reader.endObject() + assertEquals("Arrays", reader.nextName()) + reader.beginArray() + assertEquals("b", reader.nextString()) + assertEquals("c", reader.nextString()) + reader.endArray() + assertEquals("Collection", reader.nextName()) + reader.beginArray() + assertEquals("a", reader.nextString()) + assertEquals("b", reader.nextString()) + reader.endArray() + assertEquals("JsonSerializable", reader.nextName()) + assertEquals(BasicSerializable(), reader.nextOrNull(logger, BasicSerializable.Deserializer())) + assertEquals("TimeZone", reader.nextName()) + assertEquals(TimeZone.getTimeZone("Vienna"), reader.nextTimeZoneOrNull(logger)) + assertEquals("string", reader.nextName()) + assertEquals("string", reader.nextString()) + assertEquals("date", reader.nextName()) + assertEquals(Date(0), reader.nextDateOrNull(logger)) + assertEquals("number", reader.nextName()) + assertEquals(BigDecimal(123), reader.nextObjectOrNull()) + assertEquals("double", reader.nextName()) + assertEquals(Double.MAX_VALUE, reader.nextDoubleOrNull()) + assertEquals("long", reader.nextName()) + assertEquals(Long.MAX_VALUE, reader.nextLongOrNull()) + assertEquals("boolean", reader.nextName()) + assertEquals(true, reader.nextBoolean()) + assertEquals("int", reader.nextName()) + assertEquals(1, reader.nextInt()) + assertEquals("null", reader.nextName()) + reader.nextNull() + reader.endObject() + } +} diff --git a/sentry/src/test/resources/json/replay_recording.json b/sentry/src/test/resources/json/replay_recording.json new file mode 100644 index 00000000000..287419e1ebf --- /dev/null +++ b/sentry/src/test/resources/json/replay_recording.json @@ -0,0 +1,2 @@ +{"segment_id":0} +[{"type":4,"timestamp":1234567890,"data":{"href":"https://sentry.io","height":1920,"width":1080}},{"type":5,"timestamp":12345678901,"data":{"tag":"video","payload":{"segmentId":0,"size":4000000,"duration":5000,"encoding":"h264","container":"mp4","height":1920,"width":1080,"frameCount":5,"frameRate":1,"frameRateType":"constant","left":100,"top":100}}}] diff --git a/sentry/src/test/resources/json/replay_recording_payload.json b/sentry/src/test/resources/json/replay_recording_payload.json new file mode 100644 index 00000000000..fe790b52d6b --- /dev/null +++ b/sentry/src/test/resources/json/replay_recording_payload.json @@ -0,0 +1,32 @@ +[ + { + "type": 4, + "timestamp": 1234567890, + "data": { + "href": "https://sentry.io", + "height": 1920, + "width": 1080 + } + }, + { + "type": 5, + "timestamp": 12345678901, + "data": { + "tag": "video", + "payload": { + "segmentId": 0, + "size": 4000000, + "duration": 5000, + "encoding":"h264", + "container":"mp4", + "height": 1920, + "width": 1080, + "frameCount": 5, + "frameRate": 1, + "frameRateType": "constant", + "left": 100, + "top": 100 + } + } + } +] diff --git a/sentry/src/test/resources/json/rrweb_event.json b/sentry/src/test/resources/json/rrweb_event.json new file mode 100644 index 00000000000..d5610238e97 --- /dev/null +++ b/sentry/src/test/resources/json/rrweb_event.json @@ -0,0 +1,4 @@ +{ + "type": 5, + "timestamp": 9999999 +} diff --git a/sentry/src/test/resources/json/rrweb_meta_event.json b/sentry/src/test/resources/json/rrweb_meta_event.json new file mode 100644 index 00000000000..5eb561a78d1 --- /dev/null +++ b/sentry/src/test/resources/json/rrweb_meta_event.json @@ -0,0 +1,9 @@ +{ + "type": 4, + "timestamp": 1234567890, + "data": { + "href": "https://sentry.io", + "height": 1920, + "width": 1080 + } +} diff --git a/sentry/src/test/resources/json/rrweb_video_event.json b/sentry/src/test/resources/json/rrweb_video_event.json new file mode 100644 index 00000000000..692dafe879e --- /dev/null +++ b/sentry/src/test/resources/json/rrweb_video_event.json @@ -0,0 +1,21 @@ +{ + "type": 5, + "timestamp": 12345678901, + "data": { + "tag": "video", + "payload": { + "segmentId": 0, + "size": 4000000, + "duration": 5000, + "encoding":"h264", + "container":"mp4", + "height": 1920, + "width": 1080, + "frameCount": 5, + "frameRate": 1, + "frameRateType": "constant", + "left": 100, + "top": 100 + } + } +} diff --git a/sentry/src/test/resources/json/sentry_replay_event.json b/sentry/src/test/resources/json/sentry_replay_event.json new file mode 100644 index 00000000000..dea5a99e59c --- /dev/null +++ b/sentry/src/test/resources/json/sentry_replay_event.json @@ -0,0 +1,239 @@ +{ + "type": "replay_event", + "replay_type": "session", + "segment_id": 0, + "timestamp": "1942-07-09T12:55:34.000Z", + "replay_id": "f715e1d64ef64ea3ad7744b5230813c3", + "replay_start_timestamp": "1942-07-09T12:55:34.000Z", + "urls": + [ + "ScreenOne" + ], + "error_ids": + [ + "ab3a347a4cc14fd4b4cf1dc56b670c5b" + ], + "trace_ids": + [ + "340cfef948204549ac07c3b353c81c50" + ], + "event_id": "afcb46b1140ade5187c4bbb5daa804df", + "contexts": + { + "app": + { + "app_identifier": "3b7a3313-53b4-43f4-a6a1-7a7c36a9b0db", + "app_start_time": "1918-11-17T07:46:04.000Z", + "device_app_hash": "3d1fcf36-2c25-4378-bdf8-1e65239f1df4", + "build_type": "d78c56cd-eb0f-4213-8899-cd10ddf20763", + "app_name": "873656fd-f620-4edf-bb7a-a0d13325dba0", + "app_version": "801aab22-ad4b-44fb-995c-bacb5387e20c", + "app_build": "660f0cde-eedb-49dc-a973-8aa1c04f4a28", + "permissions": + { + "WRITE_EXTERNAL_STORAGE": "not_granted", + "CAMERA": "granted" + }, + "in_foreground": true, + "view_names": ["MainActivity", "SidebarActivity"] + }, + "browser": + { + "name": "e1c723db-7408-4043-baa7-f4e96234e5dc", + "version": "724a48e9-2d35-416b-9f79-132beba2473a" + }, + "device": + { + "name": "83f1de77-fdb0-470e-8249-8f5c5d894ec4", + "manufacturer": "e21b2405-e378-4a0b-ad2c-4822d97cd38c", + "brand": "1abbd13e-d1ca-4d81-bd1b-24aa2c339cf9", + "family": "67a4b8ea-6c38-4c33-8579-7697f538685c", + "model": "d6ca2f35-bcc5-4dd3-ad64-7c3b585e02fd", + "model_id": "d3f133bd-b0a2-4aa4-9eed-875eba93652e", + "archs": + [ + "856e5da3-774c-4663-a830-d19f0b7dbb5b", + "b345bd5a-90a5-4301-a5a2-6c102d7589b6", + "fd7ed64e-a591-49e0-8dc1-578234356d23", + "8cec4101-0305-480b-91ee-f3c007f668c3", + "22583b9b-195e-49bf-bfe8-825ae3a346f2", + "8675b7aa-5b94-42d0-bc14-72ea1bb7112e" + ], + "battery_level": 0.45770407, + "charging": false, + "online": true, + "orientation": "portrait", + "simulator": true, + "memory_size": -6712323365568152393, + "free_memory": -953384122080236886, + "usable_memory": -8999512249221323968, + "low_memory": false, + "storage_size": -3227905175393990709, + "free_storage": -3749039933924297357, + "external_storage_size": -7739608324159255302, + "external_free_storage": -1562576688560812557, + "screen_width_pixels": 1101873181, + "screen_height_pixels": 1902392170, + "screen_density": 0.9829039, + "screen_dpi": -2092079070, + "boot_time": "2004-11-04T08:38:00.000Z", + "timezone": "Europe/Vienna", + "id": "e0fa5c8d-83f5-4e70-bc60-1e82ad30e196", + "language": "6dd45f60-111d-42d8-9204-0452cc836ad8", + "connection_type": "9ceb3a6c-5292-4ed9-8665-5732495e8ed4", + "battery_temperature": 0.14775127, + "processor_count": 4, + "processor_frequency": 800.0, + "cpu_description": "cpu0" + }, + "gpu": + { + "name": "d623a6b5-e1ab-4402-931b-c06f5a43a5ae", + "id": -596576280, + "vendor_id": "1874778041", + "vendor_name": "d732cf76-07dc-48e2-8920-96d6bfc2439d", + "memory_size": -1484004451, + "api_type": "95dfc8bc-88ae-4d66-b85f-6c88ad45b80f", + "multi_threaded_rendering": true, + "version": "3f3f73c3-83a2-423a-8a6f-bb3de0d4a6ae", + "npot_support": "e06b074a-463c-45de-a959-cbabd461d99d" + }, + "os": + { + "name": "686a11a8-eae7-4393-aa10-a1368d523cb2", + "version": "3033f32d-6a27-4715-80c8-b232ce84ca61", + "raw_description": "eb2d0c5e-f5d4-49c7-b876-d8a654ee87cf", + "build": "bd197b97-eb68-49c3-9d07-ef789caf3069", + "kernel_version": "1df24aec-3a6f-49a9-8b50-69ae5f9dde08", + "rooted": true + }, + "response": + { + "cookies": "PHPSESSID=298zf09hf012fh2; csrftoken=u32t4o3tb3gg43; _gat=1;", + "headers": { + "content-type": "text/html" + }, + "status_code": 500, + "body_size": 1000, + "data": + { + "d9d709db-b666-40cc-bcbb-093bb12aad26": "1631d0e6-96b7-4632-85f8-ef69e8bcfb16" + }, + "arbitrary_field": "arbitrary" + }, + "runtime": + { + "name": "4ed019c4-9af9-43e0-830e-bfde9fe4461c", + "version": "16534f6b-1670-4bb8-aec2-647a1b97669b", + "raw_description": "773b5b05-a0f9-4ee6-9f3b-13155c37ad6e" + }, + "trace": + { + "trace_id": "afcb46b1140ade5187c4bbb5daa804df", + "span_id": "bf6b582d-8ce3-412b-a334-f4c5539b9602", + "parent_span_id": "c7500f2a-d4e6-4f5f-a0f4-6bb67e98d5a2", + "op": "e481581d-35a4-4e97-8a1c-b554bf49f23e", + "description": "c204b6c7-9753-4d45-927d-b19789bfc9a5", + "status": "resource_exhausted", + "origin": "auto.test.unit.spancontext", + "tags": + { + "2a5fa3f5-7b87-487f-aaa5-84567aa73642": "4781d51a-c5af-47f2-a4ed-f030c9b3e194", + "29106d7d-7fa4-444f-9d34-b9d7510c69ab": "218c23ea-694a-497e-bf6d-e5f26f1ad7bd", + "ba9ce913-269f-4c03-882d-8ca5e6991b14": "35a74e90-8db8-4610-a411-872cbc1030ac" + } + } + }, + "sdk": + { + "name": "3e934135-3f2b-49bc-8756-9f025b55143e", + "version": "3e31738e-4106-42d0-8be2-4a3a1bc648d3", + "packages": + [ + { + "name": "b59a1949-9950-4203-b394-ddd8d02c9633", + "version": "3d7790f3-7f32-43f7-b82f-9f5bc85205a8" + } + ], + "integrations": + [ + "daec50ae-8729-49b5-82f7-991446745cd5", + "8fc94968-3499-4a2c-b4d7-ecc058d9c1b0" + ] + }, + "request": + { + "url": "67369bc9-64d3-4d31-bfba-37393b145682", + "method": "8185abc3-5411-4041-a0d9-374180081044", + "query_string": "e3dc7659-f42e-413c-a07c-52b24bf9d60d", + "data": + { + "d9d709db-b666-40cc-bcbb-093bb12aad26": "1631d0e6-96b7-4632-85f8-ef69e8bcfb16" + }, + "cookies": "d84f4cfc-5310-4818-ad4f-3f8d22ceaca8", + "headers": + { + "c4991f66-9af9-4914-ac5e-e4854a5a4822": "37714d22-25a7-469b-b762-289b456fbec3" + }, + "env": + { + "6d569c89-5d5e-40e0-a4fc-109b20a53778": "ccadf763-44e4-475c-830c-de6ba0dbd202" + }, + "other": + { + "669ff1c1-517b-46dc-a889-131555364a56": "89043294-f6e1-4e2e-b152-1fdf9b1102fc" + }, + "fragment": "fragment", + "body_size": 1000, + "api_target": "graphql" + }, + "tags": + { + "79ba41db-8dc6-4156-b53e-6cf6d742eb88": "690ce82f-4d5d-4d81-b467-461a41dd9419" + }, + "release": "be9b8133-72f5-497b-adeb-b0a245eebad6", + "environment": "89204175-e462-4628-8acb-3a7fa8d8da7d", + "platform": "38decc78-2711-4a6a-a0be-abb61bfa5a6e", + "user": + { + "email": "c4d61c1b-c144-431e-868f-37a46be5e5f2", + "id": "efb2084b-1871-4b59-8897-b4bd9f196a01", + "username": "60c05dff-7140-4d94-9a61-c9cdd9ca9b96", + "ip_address": "51d22b77-f663-4dbe-8103-8b749d1d9a48", + "name": "c8c60762-b1cf-11ed-afa1-0242ac120002", + "geo": { + "city": "0e6ed0b0-b1c5-11ed-afa1-0242ac120002", + "country_code": "JP", + "region": "273a3d0a-b1c5-11ed-afa1-0242ac120002" + }, + "data": + { + "dc2813d0-0f66-4a3f-a995-71268f61a8fa": "991659ad-7c59-4dd3-bb89-0bd5c74014bd" + } + }, + "dist": "27022a08-aace-40c6-8d0a-358a27fcaa7a", + "debug_meta": + { + "sdk_info": + { + "sdk_name": "182c4407-c1e1-4427-9b5a-ad2e22b1046a", + "version_major": 2045114005, + "version_minor": 1436566288, + "version_patchlevel": 1637914973 + }, + "images": + [ + { + "uuid": "8994027e-1cd9-4be8-b611-88ce08cf16e6", + "type": "fd6e053b-a7fe-4754-916e-bfb3ab77177d", + "debug_id": "8c653f5a-3418-4823-ba91-29a84c9c1235", + "debug_file": "55cc15dd-51f3-4cad-803c-6fd90eac21f6", + "code_id": "01230ece-f729-4af4-8b48-df74700aa4bf", + "code_file": "415c8995-1cb4-4bed-ba5c-5b3d6ba1ad47", + "image_addr": "8a258c81-641d-4e54-b06e-a0f56b1ee2ef", + "image_size": -7905338721846826571, + "arch": "d00d5bea-fb5c-43c9-85f0-dc1350d957a4" + } + ] + } +}