Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[apollo-tooling] bump bootstraped version of Apollo to 4.0.0-beta.3 #5452

Merged
merged 14 commits into from
Dec 15, 2023
Merged
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.4-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 @@ -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