Skip to content

Commit 097342a

Browse files
committed
Add the network details options as 'tags' on the replay
1 parent e4c361e commit 097342a

File tree

3 files changed

+112
-0
lines changed

3 files changed

+112
-0
lines changed

sentry/src/main/java/io/sentry/rrweb/RRWebOptionsEvent.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ public RRWebOptionsEvent(final @NotNull SentryOptions options) {
6161
optionsPayload.put("screenshotStrategy", screenshotStrategy);
6262
optionsPayload.put(
6363
"networkDetailHasUrls", replayOptions.getNetworkDetailAllowUrls().length > 0);
64+
65+
// Add network detail configuration options
66+
if (replayOptions.getNetworkDetailAllowUrls().length > 0) {
67+
optionsPayload.put("networkDetailAllowUrls", replayOptions.getNetworkDetailAllowUrls());
68+
69+
optionsPayload.put("networkRequestHeaders", replayOptions.getNetworkRequestHeaders());
70+
optionsPayload.put("networkResponseHeaders", replayOptions.getNetworkResponseHeaders());
71+
optionsPayload.put("networkCaptureBodies", replayOptions.isNetworkCaptureBodies());
72+
73+
if (replayOptions.getNetworkDetailDenyUrls().length > 0) {
74+
optionsPayload.put("networkDetailDenyUrls", replayOptions.getNetworkDetailDenyUrls());
75+
}
76+
}
6477
}
6578

6679
@NotNull

sentry/src/test/java/io/sentry/rrweb/RRWebOptionsEventSerializationTest.kt

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import io.sentry.SentryReplayOptions.SentryReplayQuality.LOW
66
import io.sentry.protocol.SdkVersion
77
import io.sentry.protocol.SerializationUtils
88
import kotlin.test.assertEquals
9+
import kotlin.test.assertFalse
10+
import kotlin.test.assertTrue
11+
import kotlin.test.assertContentEquals
912
import org.junit.Test
1013
import org.mockito.kotlin.mock
1114

@@ -48,4 +51,99 @@ class RRWebOptionsEventSerializationTest {
4851
val actualJson = SerializationUtils.serializeToString(actual, fixture.logger)
4952
assertEquals(expectedJson, actualJson)
5053
}
54+
55+
@Test
56+
fun `network detail fields are not included when networkDetailAllowUrls is empty`() {
57+
val options = SentryOptions().apply {
58+
sessionReplay.setNetworkDetailAllowUrls(emptyArray())
59+
60+
// Any config is ignored when no allowUrls are specified.
61+
sessionReplay.setNetworkDetailDenyUrls(arrayOf("https://internal.example.com/*"))
62+
sessionReplay.setNetworkRequestHeaders(listOf("Authorization", "X-Custom"))
63+
sessionReplay.setNetworkResponseHeaders(listOf("X-RateLimit", "Content-Type"))
64+
}
65+
val event = RRWebOptionsEvent(options)
66+
67+
val payload = event.optionsPayload
68+
assertFalse(payload.containsKey("networkDetailAllowUrls"))
69+
assertFalse(payload.containsKey("networkDetailDenyUrls"))
70+
assertFalse(payload.containsKey("networkRequestHeaders"))
71+
assertFalse(payload.containsKey("networkResponseHeaders"))
72+
assertFalse(payload.containsKey("networkCaptureBodies"))
73+
assertEquals(false, payload["networkDetailHasUrls"])
74+
}
75+
76+
@Test
77+
fun `networkDetailAllowUrls and headers are included when networkDetailAllowUrls is configured`() {
78+
val options = SentryOptions().apply {
79+
sessionReplay.setNetworkDetailAllowUrls(arrayOf("https://api.example.com/*"))
80+
sessionReplay.setNetworkRequestHeaders(listOf("Authorization", "X-Custom"))
81+
sessionReplay.setNetworkResponseHeaders(listOf("X-RateLimit", "Content-Type"))
82+
}
83+
val event = RRWebOptionsEvent(options)
84+
85+
val payload = event.optionsPayload
86+
assertTrue(payload.containsKey("networkDetailAllowUrls"))
87+
assertTrue(payload.containsKey("networkRequestHeaders"))
88+
assertTrue(payload.containsKey("networkResponseHeaders"))
89+
assertEquals(true, payload["networkDetailHasUrls"])
90+
assertContentEquals(arrayOf("https://api.example.com/*"), payload["networkDetailAllowUrls"] as Array<String>)
91+
assertContentEquals(arrayOf("Content-Type", "Content-Length", "Accept", "Authorization", "X-Custom"), payload["networkRequestHeaders"] as Array<String>)
92+
assertContentEquals(arrayOf("Content-Type", "Content-Length", "Accept", "X-RateLimit"), payload["networkResponseHeaders"] as Array<String>)
93+
}
94+
95+
@Test
96+
fun `networkDetailDenyUrls are included when networkDetailAllowUrls is configured`() {
97+
val options = SentryOptions().apply {
98+
sessionReplay.setNetworkDetailAllowUrls(arrayOf("https://api.example.com/*"))
99+
sessionReplay.setNetworkDetailDenyUrls(arrayOf("https://internal.example.com/*"))
100+
}
101+
val event = RRWebOptionsEvent(options)
102+
103+
val payload = event.optionsPayload
104+
assertTrue(payload.containsKey("networkDetailAllowUrls"))
105+
assertTrue(payload.containsKey("networkDetailDenyUrls"))
106+
assertContentEquals(arrayOf("https://api.example.com/*"), payload["networkDetailAllowUrls"] as Array<String>)
107+
assertContentEquals(arrayOf("https://internal.example.com/*"), payload["networkDetailDenyUrls"] as Array<String>)
108+
}
109+
110+
@Test
111+
fun `networkCaptureBodies is included when networkDetailAllowUrls is configured`() {
112+
val options = SentryOptions().apply {
113+
sessionReplay.setNetworkDetailAllowUrls(arrayOf("https://api.example.com/*"))
114+
sessionReplay.setNetworkCaptureBodies(false)
115+
}
116+
val event = RRWebOptionsEvent(options)
117+
118+
val payload = event.optionsPayload
119+
assertTrue(payload.containsKey("networkCaptureBodies"))
120+
assertEquals(false, payload["networkCaptureBodies"])
121+
}
122+
123+
@Test
124+
fun `default networkCaptureBodies is included when networkDetailAllowUrls is configured`() {
125+
val options = SentryOptions().apply {
126+
sessionReplay.setNetworkDetailAllowUrls(arrayOf("https://api.example.com/*"))
127+
}
128+
val event = RRWebOptionsEvent(options)
129+
130+
val payload = event.optionsPayload
131+
assertTrue(payload.containsKey("networkCaptureBodies"))
132+
assertEquals(true, payload["networkCaptureBodies"])
133+
}
134+
135+
@Test
136+
fun `default network request and response headers are included when networkDetailAllowUrls is configured but no custom headers set`() {
137+
val options = SentryOptions().apply {
138+
sessionReplay.setNetworkDetailAllowUrls(arrayOf("https://api.example.com/*"))
139+
// No custom headers set, should use defaults only
140+
}
141+
val event = RRWebOptionsEvent(options)
142+
143+
val payload = event.optionsPayload
144+
assertTrue(payload.containsKey("networkRequestHeaders"))
145+
assertTrue(payload.containsKey("networkResponseHeaders"))
146+
assertContentEquals(arrayOf("Content-Type", "Content-Length", "Accept"), payload["networkRequestHeaders"] as Array<String>)
147+
assertContentEquals(arrayOf("Content-Type", "Content-Length", "Accept"), payload["networkResponseHeaders"] as Array<String>)
148+
}
51149
}

sentry/src/test/resources/json/rrweb_options_event.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"unmaskedViewClasses": ["com.example.MyClass"],
88
"nativeSdkVersion": "7.19.1",
99
"errorSampleRate": 0.1,
10+
"networkDetailHasUrls": false,
1011
"maskAllImages": false,
1112
"maskAllText": false,
1213
"maskedViewClasses": [],

0 commit comments

Comments
 (0)