Skip to content

Commit

Permalink
introduce JsonReader.toApolloResponse
Browse files Browse the repository at this point in the history
  • Loading branch information
martinbonnin committed Sep 1, 2023
1 parent c435d6e commit 898816b
Show file tree
Hide file tree
Showing 18 changed files with 162 additions and 115 deletions.
5 changes: 5 additions & 0 deletions libraries/apollo-api/api/apollo-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,11 @@ public final class com/apollographql/apollo3/api/Operations {
public static final fun parseJsonResponse (Lcom/apollographql/apollo3/api/Operation;Lcom/apollographql/apollo3/api/json/JsonReader;Lcom/apollographql/apollo3/api/CustomScalarAdapters;)Lcom/apollographql/apollo3/api/ApolloResponse;
public static final fun parseJsonResponse (Lcom/apollographql/apollo3/api/Operation;Lcom/apollographql/apollo3/api/json/JsonReader;Lcom/apollographql/apollo3/api/CustomScalarAdapters;Ljava/util/Set;)Lcom/apollographql/apollo3/api/ApolloResponse;
public static synthetic fun parseJsonResponse$default (Lcom/apollographql/apollo3/api/Operation;Lcom/apollographql/apollo3/api/json/JsonReader;Lcom/apollographql/apollo3/api/CustomScalarAdapters;Ljava/util/Set;ILjava/lang/Object;)Lcom/apollographql/apollo3/api/ApolloResponse;
public static final fun parseResponse (Lcom/apollographql/apollo3/api/Operation;Lcom/apollographql/apollo3/api/json/JsonReader;)Lcom/apollographql/apollo3/api/ApolloResponse;
public static final fun parseResponse (Lcom/apollographql/apollo3/api/Operation;Lcom/apollographql/apollo3/api/json/JsonReader;Ljava/util/UUID;)Lcom/apollographql/apollo3/api/ApolloResponse;
public static final fun parseResponse (Lcom/apollographql/apollo3/api/Operation;Lcom/apollographql/apollo3/api/json/JsonReader;Ljava/util/UUID;Lcom/apollographql/apollo3/api/CustomScalarAdapters;)Lcom/apollographql/apollo3/api/ApolloResponse;
public static final fun parseResponse (Lcom/apollographql/apollo3/api/Operation;Lcom/apollographql/apollo3/api/json/JsonReader;Ljava/util/UUID;Lcom/apollographql/apollo3/api/CustomScalarAdapters;Ljava/util/Set;)Lcom/apollographql/apollo3/api/ApolloResponse;
public static synthetic fun parseResponse$default (Lcom/apollographql/apollo3/api/Operation;Lcom/apollographql/apollo3/api/json/JsonReader;Ljava/util/UUID;Lcom/apollographql/apollo3/api/CustomScalarAdapters;Ljava/util/Set;ILjava/lang/Object;)Lcom/apollographql/apollo3/api/ApolloResponse;
}

public abstract class com/apollographql/apollo3/api/Optional {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
@file:JvmName("Assertions")
package com.apollographql.apollo3.api

import com.apollographql.apollo3.exception.ApolloException
import com.apollographql.apollo3.exception.DefaultApolloException
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
import kotlin.jvm.JvmName

/**
* Helper function for the JavaCodegen
*/
fun checkFieldNotMissing(value: Any?, name: String) {
if (value == null) {
throw DefaultApolloException("Field $name is missing")
throw DefaultApolloException("Field '$name' is missing")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import com.apollographql.apollo3.annotations.ApolloExperimental
import com.apollographql.apollo3.api.internal.ResponseParser
import com.apollographql.apollo3.api.json.JsonReader
import com.apollographql.apollo3.api.json.JsonWriter
import com.apollographql.apollo3.api.json.jsonReader
import com.apollographql.apollo3.api.json.writeObject
import com.apollographql.apollo3.exception.ApolloException
import com.apollographql.apollo3.exception.ApolloParseException
import com.apollographql.apollo3.exception.JsonDataException
import com.apollographql.apollo3.exception.JsonEncodingException
import okio.Buffer
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import okio.IOException
import okio.use
import kotlin.jvm.JvmName
Expand Down Expand Up @@ -46,7 +48,8 @@ fun <D : Operation.Data> Operation<D>.composeJsonRequest(
}

/**
* Reads a GraphQL Json response like below to a [ApolloResponse]
* Reads a GraphQL Json response to a [ApolloResponse]. GraphQL Json responses look like so:
*
* ```
* {
* "data": ...
Expand All @@ -62,26 +65,65 @@ fun <D : Operation.Data> Operation<D>.composeJsonRequest(
* @throws JsonDataException if the data is not of the expected type
*/
@JvmOverloads
@Deprecated("Use parseResponse or jsonReader.toApolloResponse() instead", ReplaceWith("parseResponse"))
fun <D : Operation.Data> Operation<D>.parseJsonResponse(
jsonReader: JsonReader,
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
deferredFragmentIdentifiers: Set<DeferredFragmentIdentifier>? = null,
): ApolloResponse<D> {
return ResponseParser.parse(
jsonReader,
this,
customScalarAdapters,
deferredFragmentIdentifiers,
)
return jsonReader.use {
ResponseParser.parse(
it,
this,
null,
customScalarAdapters,
deferredFragmentIdentifiers,
)
}
}

/**
* Reads a GraphQL Json response like below to a [ApolloResponse]. GraphQL Json responses look like so:
*
* ```
* {
* "data": ...
* "errors": ...
* "extensions": ...
* }
* ```
*
* By default, this method does not close the [jsonReader]
*/
@JvmOverloads
@ApolloExperimental
fun <D : Operation.Data> Operation<D>.parseJsonResponse(
json: String,
fun <D : Operation.Data> Operation<D>.parseResponse(
jsonReader: JsonReader,
requestUuid: Uuid? = null,
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
deferredFragmentIdentifiers: Set<DeferredFragmentIdentifier>? = null,
): ApolloResponse<D> {
return parseJsonResponse(Buffer().writeUtf8(json).jsonReader(), customScalarAdapters)
return try {
ResponseParser.parse(
jsonReader,
this,
requestUuid,
customScalarAdapters,
deferredFragmentIdentifiers,
)
} catch (throwable: Throwable) {
val apolloException = if (throwable is ApolloException) {
throwable
} else {
// This happens for null pointer exceptions on missing fields
ApolloParseException(
message = "Failed to parse GraphQL http network response",
cause = throwable
)
}
return ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = this, exception = apolloException)
.isLast(true)
.build()
}
}

/**
Expand All @@ -103,3 +145,16 @@ fun <D : Operation.Data> Operation<D>.composeJsonResponse(
}
}


@ApolloExperimental
fun <D : Operation.Data> JsonReader.toApolloResponse(
operation: Operation<D>,
requestUuid: Uuid? = null,
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
deferredFragmentIdentifiers: Set<DeferredFragmentIdentifier>? = null,
): ApolloResponse<D> {
return use {
operation.parseResponse(it, requestUuid, customScalarAdapters, deferredFragmentIdentifiers)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import com.apollographql.apollo3.api.falseVariables
import com.apollographql.apollo3.api.json.JsonReader
import com.apollographql.apollo3.api.json.MapJsonReader
import com.apollographql.apollo3.api.json.readAny
import com.apollographql.apollo3.api.nullable
import com.apollographql.apollo3.api.parseData
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import okio.use

/**
* [ResponseParser] parses network responses, including data, errors and extensions from a [JsonReader]
Expand All @@ -21,33 +20,31 @@ internal object ResponseParser {
fun <D : Operation.Data> parse(
jsonReader: JsonReader,
operation: Operation<D>,
requestUuid: Uuid?,
customScalarAdapters: CustomScalarAdapters,
deferredFragmentIds: Set<DeferredFragmentIdentifier>?,
): ApolloResponse<D> {
@Suppress("NAME_SHADOWING")
return jsonReader.use { jsonReader ->
jsonReader.beginObject()

var data: D? = null
var errors: List<Error>? = null
var extensions: Map<String, Any?>? = null
while (jsonReader.hasNext()) {
@Suppress("UNCHECKED_CAST")
when (jsonReader.nextName()) {
"data" -> {
val falseVariables = operation.falseVariables(customScalarAdapters)
data = operation.parseData(jsonReader, customScalarAdapters, falseVariables, deferredFragmentIds)
}
"errors" -> errors = jsonReader.readErrors()
"extensions" -> extensions = jsonReader.readAny() as? Map<String, Any?>
else -> jsonReader.skipValue()
jsonReader.beginObject()

var data: D? = null
var errors: List<Error>? = null
var extensions: Map<String, Any?>? = null
while (jsonReader.hasNext()) {
@Suppress("UNCHECKED_CAST")
when (jsonReader.nextName()) {
"data" -> {
val falseVariables = operation.falseVariables(customScalarAdapters)
data = operation.parseData(jsonReader, customScalarAdapters, falseVariables, deferredFragmentIds)
}
"errors" -> errors = jsonReader.readErrors()
"extensions" -> extensions = jsonReader.readAny() as? Map<String, Any?>
else -> jsonReader.skipValue()
}
}

jsonReader.endObject()
jsonReader.endObject()

ApolloResponse.Builder(requestUuid = uuid4(), operation = operation, data = data, errors = errors, extensions = extensions).build()
}
return ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = operation, data = data, errors = errors, extensions = extensions).build()
}

fun parseError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,9 @@ public <D extends Operation.Data> void execute(@NotNull ApolloRequest<D> request
}
} else {
BufferedSourceJsonReader jsonReader = new BufferedSourceJsonReader(response.getBody());
try {
CustomScalarAdapters customScalarAdapters = request.getExecutionContext().get(CustomScalarAdapters.Key);
ApolloResponse<D> apolloResponse = Operations.parseJsonResponse(request.getOperation(), jsonReader, customScalarAdapters);
callback.onResponse(apolloResponse);
} catch (Exception e) {
callback.onResponse(getExceptionResponse(request, new ApolloParseException("Cannot parse response", e)));
}
CustomScalarAdapters customScalarAdapters = request.getExecutionContext().get(CustomScalarAdapters.Key);
ApolloResponse<D> apolloResponse = Operations.toApolloResponse(jsonReader, request.getOperation(), request.getRequestUuid(), customScalarAdapters, null);
callback.onResponse(apolloResponse);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ private void disposeSubscription(String id) {
CustomScalarAdapters customScalarAdapters = request.getExecutionContext().get(CustomScalarAdapters.Key);
JsonReader jsonReader = new MapJsonReader(payload);
//noinspection rawtypes
ApolloResponse apolloResponse = Operations.parseJsonResponse(request.getOperation(), jsonReader, customScalarAdapters).newBuilder().requestUuid(request.getRequestUuid()).build();
ApolloResponse apolloResponse = Operations.toApolloResponse(jsonReader, request.getOperation(), request.getRequestUuid(), customScalarAdapters, null);
//noinspection unchecked
subscriptionInfo.callback.onResponse(apolloResponse);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import com.apollographql.apollo3.api.http.HttpResponse
import com.apollographql.apollo3.api.json.JsonReader
import com.apollographql.apollo3.api.json.jsonReader
import com.apollographql.apollo3.api.json.readAny
import com.apollographql.apollo3.api.parseJsonResponse
import com.apollographql.apollo3.api.toApolloResponse
import com.apollographql.apollo3.exception.ApolloException
import com.apollographql.apollo3.exception.ApolloHttpException
import com.apollographql.apollo3.exception.ApolloParseException
Expand All @@ -26,7 +26,6 @@ import com.apollographql.apollo3.network.NetworkTransport
import com.benasher44.uuid.Uuid
import com.benasher44.uuid.uuid4
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
Expand Down Expand Up @@ -138,15 +137,11 @@ private constructor(
customScalarAdapters: CustomScalarAdapters,
httpResponse: HttpResponse,
): Flow<ApolloResponse<D>> {
val response = try {
operation.parseJsonResponse(
jsonReader = httpResponse.body!!.jsonReader(),
customScalarAdapters = customScalarAdapters,
deferredFragmentIdentifiers = null,
)
} catch (e: Exception) {
errorResponse(operation, e)
}
val response = httpResponse.body!!.jsonReader().toApolloResponse(
operation,
customScalarAdapters = customScalarAdapters,
deferredFragmentIdentifiers = null,
)

return flowOf(response.newBuilder().isLast(true).build())
}
Expand Down Expand Up @@ -194,11 +189,10 @@ private constructor(
reader.beginObject()
reader.nextName()

// TODO: make parseJsonResponse not close the jsonReader
operation.parseJsonResponse(
jsonReader = reader,
reader.toApolloResponse(
operation = operation,
customScalarAdapters = customScalarAdapters,
deferredFragmentIdentifiers = null,
deferredFragmentIdentifiers = null
)
}

Expand All @@ -220,15 +214,13 @@ private constructor(
if (jsonMerger!!.isEmptyPayload) {
null
} else {
operation.parseJsonResponse(
jsonReader = merged.jsonReader(),
merged.jsonReader().toApolloResponse(
operation = operation,
customScalarAdapters = customScalarAdapters,
deferredFragmentIdentifiers = deferredFragmentIds,
deferredFragmentIdentifiers = deferredFragmentIds
).newBuilder().isLast(isLast).build()
}
}
}.catch {
emit(errorResponse(operation, it))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.apollographql.apollo3.api.CustomScalarAdapters
import com.apollographql.apollo3.api.Operation
import com.apollographql.apollo3.api.http.HttpHeader
import com.apollographql.apollo3.api.json.jsonReader
import com.apollographql.apollo3.api.parseJsonResponse
import com.apollographql.apollo3.api.toApolloResponse
import com.apollographql.apollo3.exception.ApolloException
import com.apollographql.apollo3.exception.ApolloNetworkException
import com.apollographql.apollo3.exception.SubscriptionOperationException
Expand Down Expand Up @@ -311,11 +311,13 @@ private constructor(
} else {
responsePayload to null
}
val apolloResponse: ApolloResponse<D> = request.operation
.parseJsonResponse(jsonReader = payload.jsonReader(), customScalarAdapters = requestCustomScalarAdapters, deferredFragmentIdentifiers = mergedFragmentIds)
.newBuilder()
.requestUuid(request.requestUuid)
.build()
val apolloResponse: ApolloResponse<D> = payload.jsonReader().toApolloResponse(
operation = request.operation,
requestUuid = request.requestUuid,
customScalarAdapters = requestCustomScalarAdapters,
deferredFragmentIdentifiers = mergedFragmentIds
)

if (!deferredJsonMerger.hasNext) {
// Last deferred payload: reset the deferredJsonMerger for potential subsequent responses
deferredJsonMerger.reset()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@

import com.apollographql.apollo3.api.ApolloResponse
import com.apollographql.apollo3.api.CustomScalarAdapters
import com.apollographql.apollo3.api.GlobalBuilder
import com.apollographql.apollo3.api.Operation
import com.apollographql.apollo3.api.Optional
import com.apollographql.apollo3.api.json.MapJsonReader
import com.apollographql.apollo3.api.parseJsonResponse
import com.apollographql.apollo3.api.toApolloResponse
import com.apollographql.apollo3.cache.normalized.api.TypePolicyCacheKeyGenerator
import com.apollographql.apollo3.cache.normalized.api.normalize
import com.example.GetCatIncludeFalseQuery
Expand All @@ -27,7 +28,7 @@ import kotlin.test.assertNull
class IncludeTest {

private fun <D : Operation.Data> Operation<D>.parseData(data: Map<String, Any?>): ApolloResponse<D> {
return parseJsonResponse(MapJsonReader(mapOf("data" to data)))
return MapJsonReader(mapOf("data" to data)).toApolloResponse(this)
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import IdCacheKeyGenerator
import com.apollographql.apollo3.api.CustomScalarAdapters
import com.apollographql.apollo3.api.Operation
import com.apollographql.apollo3.api.parseJsonResponse
import com.apollographql.apollo3.api.toApolloResponse
import com.apollographql.apollo3.cache.normalized.api.CacheHeaders
import com.apollographql.apollo3.cache.normalized.api.CacheKey
import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory
Expand Down Expand Up @@ -248,7 +249,7 @@ class NormalizerTest {

companion object {
internal fun <D : Operation.Data> records(operation: Operation<D>, name: String): Map<String, Record> {
val response = operation.parseJsonResponse(testFixtureToJsonReader(name))
val response = testFixtureToJsonReader(name).toApolloResponse(operation)
return operation.normalize(data = response.data!!, CustomScalarAdapters.Empty, cacheKeyGenerator = IdCacheKeyGenerator)
}

Expand Down
Loading

0 comments on commit 898816b

Please sign in to comment.