dorche
01/20/2023, 10:50 PMHttpResponse.body
returns BufferedSource and I am not sure how to read it without closing the stream.
The json parsing itself is pretty simple so I am fine with manually parsing but I get an exception with my current codedorche
01/20/2023, 10:53 PMbody: BufferedSource?
...
body.use { source ->
BufferedSourceJsonReader(source).use { jsonReader ->
jsonReader.beginObject()
...
}
}
is what I have now but I get
com.apollographql.apollo3.exception.ApolloParseException: Failed to parse GraphQL http network response
at com.apollographql.apollo3.network.http.HttpNetworkTransport$Companion.wrapThrowableIfNeeded(HttpNetworkTransport.kt:238)
at com.apollographql.apollo3.network.http.HttpNetworkTransport$Companion.access$wrapThrowableIfNeeded(HttpNetworkTransport.kt:232)
at com.apollographql.apollo3.network.http.HttpNetworkTransport.singleResponse(HttpNetworkTransport.kt:101)
at com.apollographql.apollo3.network.http.HttpNetworkTransport.access$singleResponse(HttpNetworkTransport.kt:29)
at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invokeSuspend(HttpNetworkTransport.kt:83)
at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invoke(Unknown Source:12)
at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invoke(Unknown Source:10)
at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:61)
at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:230)
at kotlinx.coroutines.flow.internal.ChannelFlowOperatorImpl.flowCollect(ChannelFlow.kt:195)
at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collectTo$suspendImpl(ChannelFlow.kt:157)
at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collectTo(Unknown Source:4)
at kotlinx.coroutines.flow.internal.ChannelFlow$collectToFun$1.invokeSuspend(ChannelFlow.kt:60)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Caused by: java.lang.IllegalStateException: closed
at okio.RealBufferedSource.request(RealBufferedSource.kt:203)
at com.apollographql.apollo3.api.json.BufferedSourceJsonReader.nextNonWhitespace(BufferedSourceJsonReader.kt:808)
at com.apollographql.apollo3.api.json.BufferedSourceJsonReader.doPeek(BufferedSourceJsonReader.kt:241)
at com.apollographql.apollo3.api.json.BufferedSourceJsonReader.beginObject(BufferedSourceJsonReader.kt:97)
at com.apollographql.apollo3.api.internal.ResponseParser.parse(ResponseParser.kt:25)
at com.apollographql.apollo3.api.Operations.parseJsonResponse(Operations.kt:61)
at com.apollographql.apollo3.network.http.HttpNetworkTransport.singleResponse(HttpNetworkTransport.kt:96)
at com.apollographql.apollo3.network.http.HttpNetworkTransport.access$singleResponse(HttpNetworkTransport.kt:29)
at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invokeSuspend(HttpNetworkTransport.kt:83)
at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invoke(Unknown Source:12)
at com.apollographql.apollo3.network.http.HttpNetworkTransport$execute$1.invoke(Unknown Source:10)
at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:61)
at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:230)
at kotlinx.coroutines.flow.internal.ChannelFlowOperatorImpl.flowCollect(ChannelFlow.kt:195)
at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collectTo$suspendImpl(ChannelFlow.kt:157)
at kotlinx.coroutines.flow.internal.ChannelFlowOperator.collectTo(Unknown Source:4)
at kotlinx.coroutines.flow.internal.ChannelFlow$collectToFun$1.invokeSuspend(ChannelFlow.kt:60)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
dorche
01/20/2023, 10:53 PMmbonnin
01/20/2023, 10:55 PMHttpInterceptor
that just buffers everythingmbonnin
01/20/2023, 10:56 PMmbonnin
01/20/2023, 11:08 PMStylianos Gakis
01/20/2023, 11:13 PMdorche
01/20/2023, 11:30 PMmbonnin
01/20/2023, 11:37 PM{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [{ "line": 6, "column": 7 }],
"path": ["hero", "heroFriends", 1, "name"]
}
],
If they have this format, then you can read them from ApolloResponse.errors
mbonnin
01/20/2023, 11:39 PMIt just hit me yesterday that this must be slowing down the whole interceptor chain because it needs to actually get the response.I think there's no real way around the buffering at least some part of the response. I believe the best you can do if find the minimum amount of characters to read "eagerly" and then reconstruct some okio
Source
from thatmbonnin
01/20/2023, 11:40 PMdorche
01/20/2023, 11:40 PMmbonnin
01/20/2023, 11:41 PMmessage
just send data: null
:
{
"errors": [
{
"message": "Unauthorized",
"locations": [],
"path": []
}
],
"data": null
}
mbonnin
01/20/2023, 11:41 PMdorche
01/20/2023, 11:44 PMtype
{
"errors": [
{
"message": "Unauthorized: Access is denied.",
"extensions": {
"type": "Unauthorized"
}
}
],
"data": {}
}
I guess it means I could switch to using ApolloResponse.errors
and try infering it all from message
mbonnin
01/20/2023, 11:45 PMresponse.errors[0].extensions
(source)dorche
01/20/2023, 11:47 PM{
"errors": [
{
"message": "Unauthorized: Access is denied.",
"extensions": {
"type": "Unauthorized"
}
}
],
"data": {
"someVariable": null
}
}
mbonnin
01/20/2023, 11:47 PMmbonnin
01/20/2023, 11:48 PMdorche
01/20/2023, 11:51 PMApolloResponse.errors
right away then, foolish me coming from rest world and not realising there’s a graphql waymbonnin
01/20/2023, 11:51 PMmbonnin
01/20/2023, 11:53 PMdorche
01/20/2023, 11:54 PMApolloResponse
mbonnin
01/20/2023, 11:55 PMdorche
01/20/2023, 11:56 PMmbonnin
01/20/2023, 11:56 PMobject authInterceptor: ApolloInterceptor {
override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
return chain.proceed(request).onEach {
if (it.errors?.get(0)?.extensions?.get("type") == "UnAuthorized") {
// do something
}
}
}
}
mbonnin
01/20/2023, 11:57 PMApolloInterceptor
works at the GraphQL layer. HttpInterceptor
at the HTTP layer. So with HTTP, you deal with response bodies while with GraphQL you deal with ApolloResponse
dorche
01/21/2023, 12:06 AMApolloInterceptor
from HttpInterceptor
that I had to the above parsing.
Thanks again, it really wasn’t obvious that both existed, maybe I missed it somewhere in the docs. It should be somewhere in capitals haha
ApolloInterceptor: Use this one if you need access to the body of the response
mbonnin
01/21/2023, 12:08 AMApolloInterceptor
. Want to open an issue so we don't forget about this?dorche
01/21/2023, 12:41 AM