https://kotlinlang.org logo
Title
n

Nick

08/24/2021, 9:36 PM
Is there a good way to have the client emit a
sealed class
for the response? I’d like to emit various states such as
loading
,
failure
, and
success
but I don’t want to have to implement this on every call, I’d rather just do it in one spot 😄
m

mbonnin

08/25/2021, 12:49 PM
The main thing to be aware of is that in GraphQL, it is possible to have partial responses. So it's possible to have both errors and data
If you don't want to handle partial errors, it's perfectly fine to map all partial responses as errors
Then you can emit loading/failure/success from an Rx Observable or a coroutines Flow
n

Nick

08/25/2021, 3:44 PM
ahhh, ok, thanks! I’m thinking something like this? What do you think?
sealed class Response<out T> {
    object Loading : Response<Nothing>()
    data class Failure(
        val message: String,
        val errors: List<Error>? = null,
        val cause: Throwable? = null
    ) : Response<Nothing>()

    data class Complete<out T>(
        val value: T?,
        val errors: List<com.apollographql.apollo.api.Error>? = null
    ) : Response<T>()
}

fun <D : Operation.Data, T : Any, V : Operation.Variables> ApolloClient.stateful(
    query: Query<D, T, V>,
    retries: Long = 1,
    fetchType: ResponseFetcher = ApolloResponseFetchers.NETWORK_FIRST
): Flow<Response<T>> {
    return query(
        query
    )
        .toBuilder()
        .responseFetcher(fetchType)
        .build()
        .watcher()
        .toFlow()
        .retry(retries)
        .map {
            Response.Complete(it.data, it.errors)
        }
        .onStart {
            emit(Response.Loading)
        }
}
I’m having an issue with the
onStart
and
catch
since the I’m mapping to a
Complete
, it’s expecting that all types emitted to be
Complete
Any suggestions?
meh, close enough:
fun <D : Operation.Data, T : Any, V : Operation.Variables> ApolloClient.stateful(
    query: Query<D, T, V>,
    fetchType: ResponseFetcher = ApolloResponseFetchers.NETWORK_FIRST
) = flow{
    try {
        emit(Response.Loading)
        val response = query(query).toBuilder().responseFetcher(fetchType).build().await()
        emit(Response.Complete(response.data, response.errors))
    } catch (ex: Exception) {
        emit(Response.Failure("Error executing $query", cause = ex))
    }
}
m

mbonnin

08/25/2021, 4:15 PM
This looks promising. Is this not working?
n

Nick

08/25/2021, 4:23 PM
i haven’t ran it yet 🙂
😀 1