Skip to content

Commit

Permalink
[apollo-tooling] bump bootstraped version of Apollo to 4.0.0-beta.3 (
Browse files Browse the repository at this point in the history
…#5452)

* add a test to download the schema against a real server

* improve error handling

* Fix missing return

* Fix registerOperations error message when service is null

* Revert test

* display error.message in the exception

* Use consistent error handling pattern in PersistedQueriesUploader.kt

* Use consistent error handling pattern in downloadRegistry

* factor some code and make the error handling more beautiful

* keep flow control flat

* fix indentation

* map Void to Unit

* add comments and deprecations

* simplify

---------

Co-authored-by: BoD <BoD@JRAF.org>
  • Loading branch information
martinbonnin and BoD committed Dec 15, 2023
1 parent ed21a52 commit 690ec85
Show file tree
Hide file tree
Showing 17 changed files with 172 additions and 148 deletions.
2 changes: 1 addition & 1 deletion gradle/libraries.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ androidx-sqlite = "2.3.1"
# This is used by the gradle integration tests to get the artifacts locally
apollo = "4.0.0-beta.5-SNAPSHOT"
# Used by the apollo-tooling project which uses a published version of Apollo
apollo-published = "4.0.0-beta.2"
apollo-published = "4.0.0-beta.3"
cache = "2.0.2"
# See https://developer.android.com/jetpack/androidx/releases/compose-kotlin
compose-compiler = "1.5.5-dev-k2.0.0-Beta1-06b8ae672a4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ suspend fun executeTelemetryNetworkCall(telemetrySession: TelemetrySession) {
instanceId = telemetrySession.instanceId,
properties = telemetrySession.properties.map { it.toToolingTelemetryProperty() },
events = telemetrySession.events.map { it.toTelemetryEvent() },
).getOrThrow()
)
}

@OptIn(ApolloInternal::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ class ApolloWebSocketClosedException(
/**
* The response was received but the response code was not 200
*
* @param statusCode: the HTTP status code
* @param headers: the HTTP headers
* @param body: the HTTP error body. By default, [body] is always null. You can opt-in [exposeHttpErrorBody] in [HttpNetworkTransport]
* @param statusCode the HTTP status code
* @param headers the HTTP headers
* @param body the HTTP error body. By default, [body] is always null. You can opt-in [HttpNetworkTransport.httpExposeErrorBody]
* if you need it. If you're doing this, you **must** call [BufferedSource.close] on [body] to avoid sockets and other resources leaking.
*/
class ApolloHttpException(
Expand Down Expand Up @@ -101,7 +101,7 @@ class JsonDataException(message: String) : ApolloException(message)
*/
class ApolloParseException(message: String? = null, cause: Throwable? = null) : ApolloException(message = message, cause = cause)

class ApolloGraphQLException(val error: Error): ApolloException("GraphQL error") {
class ApolloGraphQLException(val error: Error): ApolloException("GraphQL error: '${error.message}'") {
constructor(errors: List<Error>): this(errors.first())

@Deprecated("Use error instead", level = DeprecationLevel.ERROR)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
type Query {
apolloClients: [ApolloClient!]!
"""
Returns null if an ApolloClient with the given id is not found.
"""
apolloClient(id: ID!): ApolloClient
}

Expand Down
1 change: 1 addition & 0 deletions libraries/apollo-tooling/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ apollo {
endpointUrl.set("https://graphql.api.apollographql.com/api/graphql")
schemaFile.set(file("src/main/graphql/platform-api/internal/schema.graphqls"))
}
mapScalar("Void", "kotlin.Unit", "com.apollographql.apollo3.tooling.VoidAdapter")
mapScalar("Timestamp", "java.time.Instant", "com.apollographql.apollo3.tooling.TimestampAdapter")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
extend schema @link(url: "https://specs.apollo.dev/nullability/v0.1", import: ["@catch", "CatchTo"])
extend schema @catch(to: THROW)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extend schema @link(url: "https://specs.apollo.dev/nullability/v0.1", import: ["@semanticNonNull", "@catch", "CatchTo"])
extend schema @catch(to: THROW)

extend type Service @semanticNonNull(field: "statsWindow")
extend type ServiceMutation @semanticNonNull(field: "registerOperationsWithResponse")
extend type ServiceStatsWindow @semanticNonNull(field: "fieldLatencies")
extend type RegisterOperationsMutationResponse @semanticNonNull(field: "invalidOperations")
# Not sure if errors can be null or not
# extend type InvalidOperation @semanticNonNull(field: "errors")
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extend schema @link(url: "https://specs.apollo.dev/nullability/v0.1", import: ["@semanticNonNull", "@catch", "CatchTo"])
extend schema @catch(to: THROW)

extend type GraphMutation @semanticNonNull(field: "uploadSchema")
extend type GraphMutation @semanticNonNull(field: "publishSubgraph")
extend type GraphVariant @semanticNonNull(field: "latestPublication")
extend type SchemaPublication @semanticNonNull(field: "schema")
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.apollographql.apollo3.tooling

import com.apollographql.apollo3.annotations.ApolloExperimental
import com.apollographql.apollo3.exception.ApolloGraphQLException
import com.apollographql.apollo3.exception.ApolloHttpException
import com.apollographql.apollo3.tooling.platformapi.internal.FieldLatenciesQuery
import java.time.Instant

Expand Down Expand Up @@ -32,45 +30,32 @@ object FieldInsights {
percentile = percentile,
)
).execute()
val data = response.data
return when {
data == null -> {
val cause = when (val e = response.exception!!) {
is ApolloHttpException -> {
val body = e.body?.use { it.readUtf8() } ?: ""
Exception("Cannot fetch field latencies: (code: ${e.statusCode})\n$body", e)
}

is ApolloGraphQLException -> {
Exception("Cannot fetch field latencies: ${e.errors.joinToString { it.message }}")
}

else -> {
Exception("Cannot fetch field latencies: ${e.message}", e)
}
}
FieldLatenciesResult.Error(cause = cause)
}

data.service == null && response.hasErrors() -> {
FieldLatenciesResult.Error(cause = Exception("Cannot fetch field latencies: ${response.errors!!.joinToString { it.message }}"))
}
val data = response.data
if (data == null) {
return FieldLatenciesResult.Error(cause = response.toException("Cannot fetch field latencies"))
}

else -> {
FieldLatencies(fieldLatencies = data.service?.statsWindow?.fieldLatencies?.mapNotNull {
val parentType = it.groupBy.parentType ?: return@mapNotNull null
val fieldName = it.groupBy.fieldName ?: return@mapNotNull null
val durationMs = it.metrics.fieldHistogram.durationMs ?: return@mapNotNull null
FieldLatencies.FieldLatency(
parentType = parentType,
fieldName = fieldName,
durationMs = durationMs
)
} ?: emptyList())
}
val service = data.service
if (service == null) {
// As of Dec-2023, service doesn't return an error when it is null
// Better safe than sorry though, and we still display the GraphQL errors.
return FieldLatenciesResult.Error(cause = Exception("Cannot find service $serviceId: ${response.errors?.joinToString { it.message }}}"))
}

return FieldLatencies(fieldLatencies = service.statsWindow.fieldLatencies.mapNotNull {
val parentType = it.groupBy.parentType ?: return@mapNotNull null
val fieldName = it.groupBy.fieldName ?: return@mapNotNull null
val durationMs = it.metrics.fieldHistogram.durationMs ?: return@mapNotNull null
FieldLatencies.FieldLatency(
parentType = parentType,
fieldName = fieldName,
durationMs = durationMs
)
})
}


@ApolloExperimental
sealed interface FieldLatenciesResult {
@ApolloExperimental
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,18 @@ class PersistedQuery(
)

sealed interface PublishOperationsResult
object GraphNotFound: PublishOperationsResult
class PermissionError(val message: String): PublishOperationsResult
class CannotModifyOperationBody(val message: String): PublishOperationsResult
class PublishOperationsSuccess(val added: Int, val removed: Int, val identical: Int, val updated: Int, val unaffected: Int, val name: String, val revision: Int) : PublishOperationsResult
object GraphNotFound : PublishOperationsResult
class PermissionError(val message: String) : PublishOperationsResult
class CannotModifyOperationBody(val message: String) : PublishOperationsResult
class PublishOperationsSuccess(
val added: Int,
val removed: Int,
val identical: Int,
val updated: Int,
val unaffected: Int,
val name: String,
val revision: Int,
) : PublishOperationsResult

@ApolloExperimental
fun publishOperations(
Expand All @@ -38,11 +46,15 @@ fun publishOperations(
.execute()
}

val graph1 = response.dataOrThrow().graph
val data = response.data
if (data == null) {
throw response.toException("Cannot publish operations")
}

val graph1 = data.graph
if (graph1 == null) {
return GraphNotFound
}

val ops = graph1.persistedQueryList.publishOperations
return when {
ops.onPublishOperationsResult != null -> {
Expand All @@ -57,12 +69,16 @@ fun publishOperations(
ops.onPublishOperationsResult.build.revision
)
}

ops.onPermissionError != null -> {
PermissionError(ops.onPermissionError.message)
}

ops.onCannotModifyOperationBodyError != null -> {
CannotModifyOperationBody(ops.onCannotModifyOperationBodyError.message)
}
else -> error("")

else -> error("Unknown ops: ${ops.__typename}")
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.apollo3.tooling

import com.apollographql.apollo3.annotations.ApolloDeprecatedSince
import com.apollographql.apollo3.annotations.ApolloExperimental
import com.apollographql.apollo3.api.Optional
import com.apollographql.apollo3.ast.GQLArgument
Expand All @@ -26,8 +27,6 @@ import com.apollographql.apollo3.ast.transform
import com.apollographql.apollo3.compiler.APOLLO_VERSION
import com.apollographql.apollo3.compiler.OperationIdGenerator
import com.apollographql.apollo3.compiler.operationoutput.OperationOutput
import com.apollographql.apollo3.exception.ApolloGraphQLException
import com.apollographql.apollo3.exception.ApolloHttpException
import com.apollographql.apollo3.tooling.platformapi.internal.RegisterOperationsMutation
import com.apollographql.apollo3.tooling.platformapi.internal.type.RegisteredClientIdentityInput
import com.apollographql.apollo3.tooling.platformapi.internal.type.RegisteredOperationInput
Expand Down Expand Up @@ -225,6 +224,8 @@ object RegisterOperations {
return OperationIdGenerator.Sha256.apply(normalize(), "")
}

@Deprecated("Use persisted queries and publishOperations instead. See https://www.apollographql.com/docs/graphos/operations/persisted-queries/")
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
fun registerOperations(
key: String,
graphID: String,
Expand Down Expand Up @@ -256,29 +257,21 @@ object RegisterOperations {
val response = runBlocking { call.execute() }
val data = response.data
if (data == null) {
when (val e = response.exception!!) {
is ApolloHttpException -> {
val body = e.body?.use { it.readUtf8() } ?: ""
throw Exception("Cannot push operations: (code: ${e.statusCode})\n$body", e)
}
throw response.toException("Cannot push operations")
}

is ApolloGraphQLException -> {
throw Exception("Cannot push operations: ${e.errors.joinToString { it.message }}")
}
val service = data.service
if (service == null) {
throw Exception("Cannot push operations: cannot find service '$graphID': ${response.errors?.joinToString { it.message }}")
}

else -> {
throw Exception("Cannot push operations: ${e.message}", e)
}
}
} else {
val errors = data.service?.registerOperationsWithResponse?.invalidOperations?.flatMap {
it.errors ?: emptyList()
} ?: emptyList()
val errors = data.service.registerOperationsWithResponse.invalidOperations.flatMap {
it.errors ?: emptyList()
}

check(errors.isEmpty()) {
"Cannot push operations:\n${errors.joinToString("\n")}"
}
println("Operations pushed successfully")
if (errors.isNotEmpty()) {
throw Exception("Cannot push operations:\n${errors.joinToString("\n")}")
}
println("Operations pushed successfully")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.apollographql.apollo3.ast.toFullSchemaGQLDocument
import com.apollographql.apollo3.ast.toGQLDocument
import com.apollographql.apollo3.ast.toSDL
import com.apollographql.apollo3.ast.toUtf8
import com.apollographql.apollo3.exception.ApolloHttpException
import com.apollographql.apollo3.network.okHttpClient
import com.apollographql.apollo3.tooling.SchemaHelper.reworkFullTypeFragment
import com.apollographql.apollo3.tooling.SchemaHelper.reworkInputValueFragment
Expand Down Expand Up @@ -166,21 +167,27 @@ object SchemaDownloader {
val apolloClient = ApolloClient.Builder()
.serverUrl(endpoint)
.okHttpClient(SchemaHelper.newOkHttpClient(insecure))
.httpExposeErrorBody(true)
.build()
val response = runBlocking {
apolloClient.query(DownloadSchemaQuery(graphID = graph, variant = variant))
.httpHeaders(headers.map { HttpHeader(it.key, it.value) } + HttpHeader("x-api-key", key))
.execute()
}
if (response.exception != null) throw response.exception!!
if (response.errors?.isNotEmpty() == true) {
throw Exception("Cannot retrieve document from $endpoint: ${response.errors!!.joinToString { it.message }}\nCheck graph id and variant")
val data = response.data

if (data == null) {
throw response.toException("Cannot download schema")
}

if (data.graph == null) {
throw Exception("Cannot retrieve graph '$graph': ${response.errors?.joinToString { it.message }}")
}
val document = response.data?.graph?.variant?.latestPublication?.schema?.document
check(document != null) {
"Cannot retrieve document from $endpoint\nCheck graph id and variant"

if (data.graph.variant == null) {
throw Exception("Cannot retrieve variant '$variant': ${response.errors?.joinToString { it.message }}")
}
return document
return data.graph.variant.latestPublication.schema.document
}

inline fun <reified T> Any?.cast() = this as? T
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import com.apollographql.apollo3.ast.GQLDefinition
import com.apollographql.apollo3.ast.GQLField
import com.apollographql.apollo3.ast.GQLFragmentDefinition
import com.apollographql.apollo3.ast.GQLOperationDefinition
import com.apollographql.apollo3.exception.ApolloHttpException
import com.apollographql.apollo3.network.http.DefaultHttpEngine
import com.apollographql.apollo3.network.okHttpClient
import com.apollographql.apollo3.tooling.GraphQLFeature.*
Expand Down Expand Up @@ -82,17 +81,12 @@ internal object SchemaHelper {
.httpHeaders(headers.map { HttpHeader(it.key, it.value) })
.execute()
}
response.exception?.let { e ->
if (e is ApolloHttpException) {
val body = e.body?.use { it.readUtf8() } ?: ""
throw Exception("Cannot execute pre-introspection query from $endpoint: (code: ${e.statusCode})\n$body", e)
}
throw e
}
if (response.errors?.isNotEmpty() == true) {
throw Exception("Cannot execute pre-introspection query from $endpoint: ${response.errors!!.joinToString { it.message }}")
val data = response.data
if (data == null) {
throw response.toException("Cannot execute pre-introspection query from $endpoint")
}
return response.data!!

return data
}

internal fun executeIntrospectionQuery(
Expand Down

0 comments on commit 690ec85

Please sign in to comment.