Skip to content

Commit 7a7abe5

Browse files
authored
Add apollo-5 integration (#6074)
1 parent 6e8b449 commit 7a7abe5

30 files changed

Lines changed: 2813 additions & 0 deletions

‎.github/ISSUE_TEMPLATE/bug_report_java.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ body:
1313
- sentry-jdbc
1414
- sentry-apollo
1515
- sentry-apollo-3
16+
- sentry-apollo-4
17+
- sentry-apollo-5
1618
- sentry-kotlin-extensions
1719
- sentry-opentelemetry-agent
1820
- sentry-opentelemetry-agentless

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
- Deprecate `sendDefaultPii` in favor of `dataCollection` ahead of its removal in 9.0 ([#6158](https://github.com/getsentry/sentry-java/pull/6158))
88
- Make the tombstone merge time threshold configurable via `SentryAndroidOptions.setTombstoneMergeTimeThresholdMillis` and the `io.sentry.tombstone.merge-time-threshold-millis` manifest option ([#6154](https://github.com/getsentry/sentry-java/pull/6154))
9+
- Add `sentry-apollo-5` integration for Apollo Kotlin 5, providing HTTP tracing and failed GraphQL request reporting ([#6074](https://github.com/getsentry/sentry-java/pull/6074))
910

1011
### Improvements
1112

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ Sentry SDK for Java and Android
3838
| sentry-apollo | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-apollo?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-apollo) | 21 |
3939
| sentry-apollo-3 | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-apollo-3?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-apollo-3) | 21 |
4040
| sentry-apollo-4 | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-apollo-4?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-apollo-4) | 21 |
41+
| sentry-apollo-5 | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-apollo-5?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-apollo-5) | 21 |
4142
| sentry-kotlin-extensions | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-kotlin-extensions?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-kotlin-extensions) | 21 |
4243
| sentry-ktor-client | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-ktor-client?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-ktor-client) | 21 |
4344
| sentry-servlet | [![Maven Central Version](https://img.shields.io/maven-central/v/io.sentry/sentry-servlet?style=for-the-badge&logo=sentry&color=green)](https://central.sonatype.com/artifact/io.sentry/sentry-servlet) | |

‎buildSrc/src/main/java/Config.kt‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ object Config {
6868
val SENTRY_OPENFEIGN_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.openfeign"
6969
val SENTRY_APOLLO3_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo3"
7070
val SENTRY_APOLLO4_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo4"
71+
val SENTRY_APOLLO5_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo5"
7172
val SENTRY_APOLLO_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.apollo"
7273
val SENTRY_GRAPHQL_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.graphql"
7374
val SENTRY_GRAPHQL_CORE_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.graphql-core"

‎gradle/libs.versions.toml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ apollo2-coroutines = { module = "com.apollographql.apollo:apollo-coroutines-supp
8484
apollo2-runtime = { module = "com.apollographql.apollo:apollo-runtime", version.ref = "apollo" }
8585
apollo3-kotlin = { module = "com.apollographql.apollo3:apollo-runtime", version = "3.8.6" }
8686
apollo4-kotlin = { module = "com.apollographql.apollo:apollo-runtime", version = "4.1.1" }
87+
# sentry-apollo-5 compiles against 5.0.0 (supported floor) and tests against the latest 5.x
88+
apollo5-kotlin = { module = "com.apollographql.apollo:apollo-runtime", version = "5.0.0" }
89+
apollo5-kotlin-latest = { module = "com.apollographql.apollo:apollo-runtime", version = "5.1.0" }
8790
androidx-appcompat = { module = "androidx.appcompat:appcompat", version = "1.3.0" }
8891
androidx-annotation = { module = "androidx.annotation:annotation", version = "1.9.1" }
8992
androidx-activity = { module = "androidx.activity:activity", version = "1.8.2" }
@@ -272,6 +275,8 @@ mockito-kotlin-spring7 = { module = "org.mockito.kotlin:mockito-kotlin", version
272275
mockito-inline = { module = "org.mockito:mockito-inline", version = "4.8.0" }
273276
msgpack = { module = "org.msgpack:msgpack-core", version = "0.9.8" }
274277
okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" }
278+
# apollo-runtime 5.x pulls in okhttp 5.x; mockwebserver 4.x is incompatible with that classpath
279+
okhttp-mockwebserver5 = { module = "com.squareup.okhttp3:mockwebserver", version = "5.3.0" }
275280
okio = { module = "com.squareup.okio:okio", version = "1.13.0" }
276281
roboelectric = { module = "org.robolectric:robolectric", version = "4.15" }
277282

‎sentry-apollo-5/README.md‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# sentry-apollo-5
2+
3+
This module provides HTTP tracing and failed GraphQL request reporting for [Apollo Kotlin 5](https://www.apollographql.com/docs/kotlin/).
4+
5+
Please consult the documentation on how to install and use this integration in the Sentry Docs for [Android](https://docs.sentry.io/platforms/android/integrations/apollo5/) or [Java](https://docs.sentry.io/platforms/java/tracing/instrumentation/apollo5/).
6+
7+
## Usage
8+
9+
Add `io.sentry:sentry-apollo-5` and install the integration while building the Apollo client:
10+
11+
```kotlin
12+
val apolloClient =
13+
ApolloClient.Builder()
14+
.serverUrl("https://example.com/graphql")
15+
.sentryTracing()
16+
.build()
17+
```
18+
19+
The builder extension installs the Apollo interceptor before the cache and the Sentry HTTP interceptor.
20+
21+
Apollo rejects builder HTTP interceptors when a custom `NetworkTransport` is configured, so do not use `sentryTracing()` in that case. Add `SentryApollo5Interceptor` to the client builder and `SentryApollo5HttpInterceptor` to the custom transport manually.
22+
23+
Operation metadata is propagated between these interceptors through Apollo's execution context. Apollo's default HTTP request composer copies this context automatically. A custom `HttpRequestComposer` must copy it explicitly when building the HTTP request:
24+
25+
```kotlin
26+
HttpRequest.Builder(method, url)
27+
.addExecutionContext(apolloRequest.executionContext)
28+
.build()
29+
```
30+
31+
An entirely custom `NetworkTransport` must expose the metadata to `SentryApollo5HttpInterceptor` in the same way.
32+
33+
## Known limitations
34+
35+
- Failed GraphQL request detection matches the raw JSON response body for an `errors` field.
36+
- Failed GraphQL request detection is skipped for streaming responses (`multipart/mixed`, `text/event-stream`) to avoid buffering the body; spans and breadcrumbs are still recorded for them.
37+
- WebSocket subscriptions are not instrumented.
38+
- Batching behavior depends on HTTP interceptor ordering.
39+
- Normalized-cache hits do not create HTTP spans.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
public final class io/sentry/apollo5/BuildConfig {
2+
public static final field SENTRY_APOLLO5_SDK_NAME Ljava/lang/String;
3+
public static final field VERSION_NAME Ljava/lang/String;
4+
}
5+
6+
public final class io/sentry/apollo5/SentryApollo5ClientException : java/lang/Exception {
7+
public static final field Companion Lio/sentry/apollo5/SentryApollo5ClientException$Companion;
8+
public fun <init> (Ljava/lang/String;)V
9+
}
10+
11+
public final class io/sentry/apollo5/SentryApollo5ClientException$Companion {
12+
}
13+
14+
public final class io/sentry/apollo5/SentryApollo5HttpInterceptor : com/apollographql/apollo/network/http/HttpInterceptor {
15+
public static final field Companion Lio/sentry/apollo5/SentryApollo5HttpInterceptor$Companion;
16+
public static final field DEFAULT_CAPTURE_FAILED_REQUESTS Z
17+
public fun <init> ()V
18+
public fun <init> (Lio/sentry/IScopes;)V
19+
public fun <init> (Lio/sentry/IScopes;Lio/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback;)V
20+
public fun <init> (Lio/sentry/IScopes;Lio/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback;Z)V
21+
public fun <init> (Lio/sentry/IScopes;Lio/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;)V
22+
public synthetic fun <init> (Lio/sentry/IScopes;Lio/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback;ZLjava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
23+
public fun dispose ()V
24+
public fun intercept (Lcom/apollographql/apollo/api/http/HttpRequest;Lcom/apollographql/apollo/network/http/HttpInterceptorChain;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
25+
}
26+
27+
public abstract interface class io/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback {
28+
public abstract fun execute (Lio/sentry/ISpan;Lcom/apollographql/apollo/api/http/HttpRequest;Lcom/apollographql/apollo/api/http/HttpResponse;)Lio/sentry/ISpan;
29+
}
30+
31+
public final class io/sentry/apollo5/SentryApollo5HttpInterceptor$Companion {
32+
}
33+
34+
public final class io/sentry/apollo5/SentryApollo5Interceptor : com/apollographql/apollo/interceptor/ApolloInterceptor {
35+
public fun <init> ()V
36+
public fun <init> (Lio/sentry/IScopes;)V
37+
public synthetic fun <init> (Lio/sentry/IScopes;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
38+
public fun intercept (Lcom/apollographql/apollo/api/ApolloRequest;Lcom/apollographql/apollo/interceptor/ApolloInterceptorChain;)Lkotlinx/coroutines/flow/Flow;
39+
}
40+
41+
public final class io/sentry/apollo5/SentryApolloBuilderExtensionsKt {
42+
public static final fun sentryTracing (Lcom/apollographql/apollo/ApolloClient$Builder;)Lcom/apollographql/apollo/ApolloClient$Builder;
43+
public static final fun sentryTracing (Lcom/apollographql/apollo/ApolloClient$Builder;Lio/sentry/IScopes;)Lcom/apollographql/apollo/ApolloClient$Builder;
44+
public static final fun sentryTracing (Lcom/apollographql/apollo/ApolloClient$Builder;Lio/sentry/IScopes;Z)Lcom/apollographql/apollo/ApolloClient$Builder;
45+
public static final fun sentryTracing (Lcom/apollographql/apollo/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;)Lcom/apollographql/apollo/ApolloClient$Builder;
46+
public static final fun sentryTracing (Lcom/apollographql/apollo/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;Lio/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback;)Lcom/apollographql/apollo/ApolloClient$Builder;
47+
public static final fun sentryTracing (Lcom/apollographql/apollo/ApolloClient$Builder;ZLjava/util/List;Lio/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback;)Lcom/apollographql/apollo/ApolloClient$Builder;
48+
public static synthetic fun sentryTracing$default (Lcom/apollographql/apollo/ApolloClient$Builder;Lio/sentry/IScopes;ZLjava/util/List;Lio/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback;ILjava/lang/Object;)Lcom/apollographql/apollo/ApolloClient$Builder;
49+
public static synthetic fun sentryTracing$default (Lcom/apollographql/apollo/ApolloClient$Builder;ZLjava/util/List;Lio/sentry/apollo5/SentryApollo5HttpInterceptor$BeforeSpanCallback;ILjava/lang/Object;)Lcom/apollographql/apollo/ApolloClient$Builder;
50+
}
51+

‎sentry-apollo-5/build.gradle.kts‎

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import net.ltgt.gradle.errorprone.errorprone
2+
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
3+
4+
plugins {
5+
`java-library`
6+
id("io.sentry.javadoc")
7+
alias(libs.plugins.kotlin.jvm)
8+
alias(libs.plugins.errorprone)
9+
alias(libs.plugins.gradle.versions)
10+
alias(libs.plugins.buildconfig)
11+
id("io.sentry.animalsniffer.android")
12+
}
13+
14+
tasks.withType<KotlinCompile>().configureEach {
15+
compilerOptions.jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8
16+
compilerOptions.languageVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
17+
compilerOptions.apiVersion = org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_9
18+
}
19+
20+
dependencies {
21+
api(projects.sentry)
22+
api(projects.sentryKotlinExtensions)
23+
24+
compileOnly(libs.apollo5.kotlin)
25+
compileOnly(libs.jetbrains.annotations)
26+
compileOnly(libs.nopen.annotations)
27+
errorprone(libs.errorprone.core)
28+
errorprone(libs.nopen.checker)
29+
errorprone(libs.nullaway)
30+
31+
// tests
32+
testImplementation(projects.sentryTestSupport)
33+
testImplementation(kotlin(Config.kotlinStdLib))
34+
testImplementation(libs.apollo5.kotlin.latest)
35+
testImplementation(libs.google.truth)
36+
testImplementation(libs.kotlin.test.junit)
37+
testImplementation(libs.kotlinx.coroutines)
38+
testImplementation(libs.kotlinx.coroutines.test)
39+
testImplementation(libs.mockito.kotlin)
40+
testImplementation(libs.mockito.inline)
41+
testImplementation(libs.okhttp.mockwebserver5)
42+
}
43+
44+
tasks.withType<JavaCompile>().configureEach {
45+
options.errorprone {
46+
check("NullAway", net.ltgt.gradle.errorprone.CheckSeverity.ERROR)
47+
option("NullAway:AnnotatedPackages", "io.sentry")
48+
}
49+
}
50+
51+
buildConfig {
52+
useJavaOutput()
53+
packageName("io.sentry.apollo5")
54+
buildConfigField(
55+
"String",
56+
"SENTRY_APOLLO5_SDK_NAME",
57+
"\"${Config.Sentry.SENTRY_APOLLO5_SDK_NAME}\"",
58+
)
59+
buildConfigField("String", "VERSION_NAME", "\"${project.version}\"")
60+
}
61+
62+
tasks.jar {
63+
manifest {
64+
attributes(
65+
"Sentry-Version-Name" to project.version,
66+
"Sentry-SDK-Name" to Config.Sentry.SENTRY_APOLLO5_SDK_NAME,
67+
"Sentry-SDK-Package-Name" to "maven:io.sentry:sentry-apollo-5",
68+
"Implementation-Vendor" to "Sentry",
69+
"Implementation-Title" to project.name,
70+
"Implementation-Version" to project.version,
71+
)
72+
}
73+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.sentry.apollo5
2+
3+
import com.apollographql.apollo.api.ExecutionContext
4+
5+
internal data class SentryApollo5OperationContext(
6+
val operationId: String,
7+
val operationName: String,
8+
val operationType: String,
9+
val variables: String?,
10+
) : ExecutionContext.Element {
11+
override val key: ExecutionContext.Key<*>
12+
get() = Key
13+
14+
companion object Key : ExecutionContext.Key<SentryApollo5OperationContext>
15+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.sentry.apollo5
2+
3+
/**
4+
* Used for holding an Apollo5 client error, for example. An integration that does not throw when
5+
* API returns 4xx, 5xx or the `errors` field.
6+
*/
7+
class SentryApollo5ClientException(message: String?) : Exception(message) {
8+
companion object {
9+
private const val serialVersionUID = 4312160066430858144L
10+
}
11+
}

0 commit comments

Comments
 (0)