Skip to content
Draft
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Fixes

- Do not capture failed Apollo 3, 4, and 5 requests when the GraphQL `errors` array is empty ([#6285](https://github.com/getsentry/sentry-java/pull/6285))

## 8.61.0

### Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ constructor(
}
}

private val regex: Regex by lazy { "(?i)\"errors\"\\s*:\\s*\\[".toRegex() }
// matches a non-empty `errors` array, e.g. `"errors": [{...}]`, but not `"errors": []`
private val nonEmptyErrorsRegex: Regex by lazy {
"(?i)\"errors\"\\s*:\\s*\\[\\s*[^\\]\\s]".toRegex()
}

override suspend fun intercept(request: HttpRequest, chain: HttpInterceptorChain): HttpResponse {
val activeSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span
Expand Down Expand Up @@ -358,8 +361,8 @@ constructor(
return
}

// if there response body does not have the errors field, do not raise an issue
if (body.isEmpty() || !regex.containsMatchIn(body)) {
// if the response body does not have a non-empty errors field, do not raise an issue
if (body.isEmpty() || !nonEmptyErrorsRegex.containsMatchIn(body)) {
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ class SentryApollo3InterceptorClientErrors {
]
}"""

val responseBodyEmptyErrors =
"""{
"data": {},
"errors": [
]
}"""

fun getSut(
captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS,
failedRequestTargets: List<String> = listOf(DEFAULT_PROPAGATION_TARGETS),
Expand Down Expand Up @@ -551,6 +558,14 @@ class SentryApollo3InterceptorClientErrors {
verify(fixture.scopes).captureEvent(any(), any<Hint>())
}

@Test
fun `does not capture errors if errors array is empty`() {
val sut = fixture.getSut(responseBody = fixture.responseBodyEmptyErrors)
executeQuery(sut)

verify(fixture.scopes, never()).captureEvent(any(), any<Hint>())
}

@Test
fun `capture errors swallow any exception during the error transformation`() {
val sut = fixture.getSut(responseBody = fixture.responseBodyNotOk)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ constructor(
}
}

private val regex: Regex by lazy { "(?i)\"errors\"\\s*:\\s*\\[".toRegex() }
// matches a non-empty `errors` array, e.g. `"errors": [{...}]`, but not `"errors": []`
private val nonEmptyErrorsRegex: Regex by lazy {
"(?i)\"errors\"\\s*:\\s*\\[\\s*[^\\]\\s]".toRegex()
}

override suspend fun intercept(request: HttpRequest, chain: HttpInterceptorChain): HttpResponse {
val activeSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span
Expand Down Expand Up @@ -357,8 +360,8 @@ constructor(
return
}

// if the response body does not have the errors field, do not raise an issue
if (body.isEmpty() || !regex.containsMatchIn(body)) {
// if the response body does not have a non-empty errors field, do not raise an issue
if (body.isEmpty() || !nonEmptyErrorsRegex.containsMatchIn(body)) {
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ abstract class SentryApollo4BuilderExtensionsClientErrorsTest(
]
}"""

val responseBodyEmptyErrors =
"""{
"data": {},
"errors": [
]
}"""

fun getSut(
captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS,
failedRequestTargets: List<String> = listOf(DEFAULT_PROPAGATION_TARGETS),
Expand Down Expand Up @@ -561,6 +568,14 @@ abstract class SentryApollo4BuilderExtensionsClientErrorsTest(
verify(fixture.scopes).captureEvent(any(), any<Hint>())
}

@Test
fun `does not capture errors if errors array is empty`() {
val sut = fixture.getSut(responseBody = fixture.responseBodyEmptyErrors)
executeQuery(sut)

verify(fixture.scopes, never()).captureEvent(any(), any<Hint>())
}

@Test
fun `capture errors swallow any exception during the error transformation`() {
val sut = fixture.getSut(responseBody = fixture.responseBodyNotOk)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,10 @@ constructor(
}
}

private val regex: Regex by lazy { "(?i)\"errors\"\\s*:\\s*\\[".toRegex() }
// matches a non-empty `errors` array, e.g. `"errors": [{...}]`, but not `"errors": []`
private val nonEmptyErrorsRegex: Regex by lazy {
"(?i)\"errors\"\\s*:\\s*\\[\\s*[^\\]\\s]".toRegex()
}

override suspend fun intercept(request: HttpRequest, chain: HttpInterceptorChain): HttpResponse {
val activeSpan = if (Platform.isAndroid()) scopes.transaction else scopes.span
Expand Down Expand Up @@ -367,8 +370,8 @@ constructor(
return
}

// if the response body does not have the errors field, do not raise an issue
if (body.isEmpty() || !regex.containsMatchIn(body)) {
// if the response body does not have a non-empty errors field, do not raise an issue
if (body.isEmpty() || !nonEmptyErrorsRegex.containsMatchIn(body)) {
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ abstract class SentryApollo5BuilderExtensionsClientErrorsTest(
]
}"""

val responseBodyEmptyErrors =
"""{
"data": {},
"errors": [
]
}"""

fun getSut(
captureFailedRequests: Boolean = DEFAULT_CAPTURE_FAILED_REQUESTS,
failedRequestTargets: List<String> = listOf(DEFAULT_PROPAGATION_TARGETS),
Expand Down Expand Up @@ -550,6 +557,14 @@ abstract class SentryApollo5BuilderExtensionsClientErrorsTest(
verify(fixture.scopes).captureEvent(any(), any<Hint>())
}

@Test
fun `does not capture errors if errors array is empty`() {
val sut = fixture.getSut(responseBody = fixture.responseBodyEmptyErrors)
executeQuery(sut)

verify(fixture.scopes, never()).captureEvent(any(), any<Hint>())
}

@Test
fun `capture errors swallow any exception during the error transformation`() {
val sut = fixture.getSut(responseBody = fixture.responseBodyNotOk)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ abstract class SentryApollo5HttpInterceptorTest(
private suspend fun interceptWithContentType(contentType: String?): Boolean {
var bodyRead = false
val body =
object : ForwardingSource(Buffer().writeUtf8("{\"errors\":[]}")) {
object : ForwardingSource(Buffer().writeUtf8("{\"errors\":[{\"message\":\"error\"}]}")) {
override fun read(sink: Buffer, byteCount: Long): Long {
bodyRead = true
return super.read(sink, byteCount)
Expand Down
Loading