Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
- Record XHR request/response headers and (optionally) bodies in Mobile Session Replay. Opt in via `mobileReplayIntegration` with `networkDetailAllowUrls` to capture headers; set `networkCaptureBodies: true` to also capture bodies. Other options: `networkDetailDenyUrls`, `networkRequestHeaders`, `networkResponseHeaders`. Authorization-like headers are always stripped, bodies are capped at ~150 KB. Covers XHR-based clients like `axios`; fetch will follow. See [Network Details](https://docs.sentry.io/platforms/react-native/session-replay/#network-details) for details. ([#6288](https://github.com/getsentry/sentry-react-native/pull/6288))
- Warn during dev builds when multiple versions of Sentry JS SDK are detected ([#6269](https://github.com/getsentry/sentry-react-native/pull/6269))

### Fixes

- Fix Android `ClassCastException` when syncing breadcrumbs with a numeric timestamp to the native scope ([#6308](https://github.com/getsentry/sentry-react-native/pull/6308))

### Dependencies

- Bump Android SDK from v8.43.1 to v8.43.2 ([#6273](https://github.com/getsentry/sentry-react-native/pull/6273))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,21 @@ class RNSentryBreadcrumbTest {
assertNotNull(actual.timestamp)
}

@Test
fun parsesNumericTimestampWithoutDiscardingBreadcrumb() {
// The JS SDK stamps `timestamp` as a number (epoch seconds), which arrives over the bridge
// as a Double. The native deserializer expects an ISO-8601 string, so without normalization
// this throws a ClassCastException and the whole breadcrumb is discarded (see #6306).
val map = JavaOnlyMap()
map.putString("message", "testMessage")
map.putDouble("timestamp", 1_700_000_000.5)
val actual = RNSentryBreadcrumb.fromMap(map, logger)
assertEquals("testMessage", actual.message)
assertEquals("react-native", actual.origin)
assertNotNull(actual.timestamp)
assertEquals(1_700_000_000_500L, actual.timestamp.time)
}

@Test
fun reactNativeForMissingOrigin() {
val map =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReadableType;
import io.sentry.Breadcrumb;
import io.sentry.DateUtils;
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import io.sentry.util.MapObjectReader;
import java.util.Date;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -42,7 +44,18 @@ public static String getCurrentScreenFrom(ReadableMap from) {
@NotNull
public static Breadcrumb fromMap(ReadableMap from, @NotNull ILogger logger) {
try {
final @NotNull MapObjectReader reader = new MapObjectReader(toDeepHashMap(from));
final @NotNull Map<String, Object> map = toDeepHashMap(from);

// The JS SDK stamps `timestamp` as a number (epoch seconds), which arrives over the bridge as
// a Double. The native Breadcrumb deserializer expects an ISO-8601 string, so normalize it
// before deserializing to avoid a ClassCastException.
final @Nullable Object timestamp = map.get("timestamp");
if (timestamp instanceof Number) {
final long millis = (long) (((Number) timestamp).doubleValue() * 1000);
map.put("timestamp", DateUtils.getTimestamp(new Date(millis)));
}

final @NotNull MapObjectReader reader = new MapObjectReader(map);
final @NotNull Breadcrumb breadcrumb =
new Breadcrumb.Deserializer().deserialize(reader, logger);

Expand Down
Loading