diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operations.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operations.kt index 55057e0f100..3b6c0971841 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operations.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/Operations.kt @@ -94,6 +94,8 @@ fun Operation.parseJsonResponse( * ``` * * By default, this method does not close the [jsonReader] + * + * @see [toApolloResponse] */ @JvmOverloads fun Operation.parseResponse( @@ -111,21 +113,24 @@ fun Operation.parseResponse( deferredFragmentIdentifiers, ) } catch (throwable: Throwable) { - val apolloException = if (throwable is ApolloException) { - throwable - } else { - ApolloNetworkException( - message = "Error while reading JSON response", - platformCause = throwable - ) - } - return ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = this) - .exception(exception = apolloException) + ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = this) + .exception(exception = throwable.wrapIfNeeded()) .isLast(true) .build() } } +private fun Throwable.wrapIfNeeded(): ApolloException { + return if (this is ApolloException) { + this + } else { + ApolloNetworkException( + message = "Error while reading JSON response", + platformCause = this + ) + } +} + /** * writes a successful GraphQL Json response containing "data" to the given sink. * @@ -146,9 +151,12 @@ fun Operation.composeJsonResponse( } /** - * Parses the [JsonReader] into an [ApolloResponse] + * Reads a single [ApolloResponse] from [this]. Returns an error response if [this] contains + * more than one JSON response or trailing tokens. + * [toApolloResponse] takes ownership and closes [this]. * - * Warning: this closes the [JsonReader]. If you need to reuse it, use [parseResponse] + * @return the parsed [ApolloResponse] + * @see parseResponse */ @ApolloExperimental fun JsonReader.toApolloResponse( @@ -158,10 +166,34 @@ fun JsonReader.toApolloResponse( deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { return use { - operation.parseResponse(it, requestUuid, customScalarAdapters, deferredFragmentIdentifiers) + try { + ResponseParser.parse( + this, + operation, + requestUuid, + customScalarAdapters, + deferredFragmentIdentifiers, + ).also { + if (peek() != JsonReader.Token.END_DOCUMENT) { + throw JsonDataException("Expected END_DOCUMENT but was ${peek()}") + } + } + } catch (throwable: Throwable) { + ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = operation) + .exception(exception = throwable.wrapIfNeeded()) + .isLast(true) + .build() + } } } +/** + * Reads a [ApolloResponse] from [this]. + * The caller is responsible for closing [this]. + * + * @return the parsed [ApolloResponse] + * @see [toApolloResponse] + */ @ApolloExperimental fun JsonReader.parseResponse( operation: Operation, @@ -169,6 +201,19 @@ fun JsonReader.parseResponse( customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty, deferredFragmentIdentifiers: Set? = null, ): ApolloResponse { - return operation.parseResponse(this, requestUuid, customScalarAdapters, deferredFragmentIdentifiers) + return try { + ResponseParser.parse( + this, + operation, + requestUuid, + customScalarAdapters, + deferredFragmentIdentifiers, + ) + } catch (throwable: Throwable) { + ApolloResponse.Builder(requestUuid = requestUuid ?: uuid4(), operation = operation) + .exception(exception = throwable.wrapIfNeeded()) + .isLast(true) + .build() + } }