I have a query and a response object - is there wa...
# apollo-kotlin
j
I have a query and a response object - is there way - for logging purposes - to retrieve request headers that were added automatically by custom Apollo's HttpInterceptor? Thank you.
m
I don't think you can retrieve
x-apollo-operation-name
from the
query
neither the response. You could write your own interceptor to get them though and store them in a custom
ExecutionContext
So you can use
query.name()
and
query.id()
to retrieve the same values
j
ExecutionContext seems great! I'm just looking for my custom headers.
m
Right 👍
j
I struggle a bit to find a way to add it there in my interceptor - is there a doc for execution context? Or where should I do this?
Copy code
internal class NetworkAuthorizationInterceptor(
  private val ...
) :
  HttpInterceptor {

  override suspend fun intercept(request: HttpRequest, chain: HttpInterceptorChain): HttpResponse {
    return chain.proceed(
      request
        .newBuilder()
        .addHeader("Authorization", ...)
        .build()
    )
  }
}
m
ExecutionContext
is not documented a lot, it's mostly used internally. It works the same as
CoroutineContext
, it's a type-safe map-like API
You might have to wrap the
DefaultNetorkTransport
to transfer that information from the HTTP layer to the GraphQL layer. 2min, I'll try to make an example
I think something like this would do it:
Copy code
class RequestHeaders(
      val headers: List<HttpHeader>
  ): ExecutionContext.Element {
    override val key: ExecutionContext.Key<*>
      get() = Key

    companion object Key : ExecutionContext.Key<RequestHeaders>
  }
  
  class MyNetworkTransport(serverUrl: String): NetworkTransport {
    private val httpRequestComposer = DefaultHttpRequestComposer(serverUrl)
    private val delegate = HttpNetworkTransport.Builder()
        .serverUrl(serverUrl)
        .httpEngine(DefaultHttpEngine())
        .build()
    
    override fun <D : Operation.Data> execute(request: ApolloRequest<D>): Flow<ApolloResponse<D>> {
      val customScalarAdapters = request.executionContext[CustomScalarAdapters]!!
      val httpRequest = httpRequestComposer.compose(request)

      return delegate.execute(request, httpRequest, customScalarAdapters).map { 
        it.newBuilder()
            .addExecutionContext(RequestHeaders(httpRequest.headers))
            .build()
      }
    }

    override fun dispose() {
      delegate.dispose()
    }

  }
  
  @Test
  fun executionContext() = runTest {
    val mockServer = MockServer()
    val apolloClient = ApolloClient.Builder()
        .networkTransport(MyNetworkTransport(mockServer.url()))
        .build()

    apolloClient.query(EpisodeHeroNameQuery(Episode.EMPIRE))
  }
j
Thank you! I'm thinking about exposing whole httpRequest - could be there any issue?
m
You'd be holding
HttpRequest.body
longer than needed. This is a non-trivial object (contains an Okio source, etc...) so it might take some memory
Not the end of the world but I wouldn't recommend exposing it
j
Thank you very much! :) I see. I'm debugging some backend issue and need an extensive conditional logging. Maybe - is there a way to convert query to actual string to be able to log it later (and not being forced to expose httpRequest for all requests)?
m
I mean if it's only for debug you can certainly leak all the memory you want 😄
Just disable it in production builds 😅
j
Well, I'm using my users to debug it for me 😄
m
ahah
Then if you need the data for logging then there's no real way around
You could convert to a String but not sure how much you'd save
Most of the data is the actual bytes
j
The point is my toString() would be just for single query, instead of leaking all graphql requests.
m
And it's not huge, just not required in a typical application but if you need it then I guess you can expose
httpRequest
completely
j
Anyway, thank you! I have options now 🙂
m
Sure thing!
207 Views