All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.apollographql.cache.normalized.FetchPolicyInterceptors.kt Maven / Gradle / Ivy

@file:JvmName("FetchPolicyInterceptors")
@file:Suppress("DEPRECATION") // for ApolloCompositeException, see https://youtrack.jetbrains.com/issue/KT-30155

package com.apollographql.cache.normalized

import com.apollographql.apollo.api.ApolloRequest
import com.apollographql.apollo.api.ApolloResponse
import com.apollographql.apollo.api.Operation
import com.apollographql.apollo.api.Query
import com.apollographql.apollo.conflateFetchPolicyInterceptorResponses
import com.apollographql.apollo.exception.ApolloCompositeException
import com.apollographql.apollo.exception.ApolloException
import com.apollographql.apollo.exception.CacheMissException
import com.apollographql.apollo.exception.DefaultApolloException
import com.apollographql.apollo.interceptor.ApolloInterceptor
import com.apollographql.apollo.interceptor.ApolloInterceptorChain
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.single
import kotlin.jvm.JvmName

/**
 * An interceptor that emits the response from the cache only.
 */
val CacheOnlyInterceptor = object : ApolloInterceptor {
  override fun  intercept(request: ApolloRequest, chain: ApolloInterceptorChain): Flow> {
    return chain.proceed(
        request = request
            .newBuilder()
            .fetchFromCache(true)
            .build()
    )
  }
}

/**
 * An interceptor that emits the response(s) from the network only.
 */
val NetworkOnlyInterceptor = object : ApolloInterceptor {
  override fun  intercept(request: ApolloRequest, chain: ApolloInterceptorChain): Flow> {
    return chain.proceed(request)
  }
}

/**
 * An interceptor that emits the response from the cache first, and if there was a cache miss, emits the response(s) from the network.
 */
val CacheFirstInterceptor = object : ApolloInterceptor {
  override fun  intercept(request: ApolloRequest, chain: ApolloInterceptorChain): Flow> {
    return flow {
      val cacheResponse = chain.proceed(
          request = request
              .newBuilder()
              .fetchFromCache(true)
              .build()
      ).single()
      emit(cacheResponse.newBuilder().isLast(cacheResponse.exception == null).build())
      if (cacheResponse.exception == null) {
        return@flow
      }

      val networkResponses = chain.proceed(request = request)
      emitAll(networkResponses)
    }
  }
}

/**
 * An interceptor that emits the response(s) from the network first, and if there was a network error, emits the response from the cache.
 */
val NetworkFirstInterceptor = object : ApolloInterceptor {
  override fun  intercept(request: ApolloRequest, chain: ApolloInterceptorChain): Flow> {
    return flow {
      var networkException: ApolloException? = null

      val networkResponses = chain.proceed(
          request = request
      ).onEach { response ->
        if (response.exception != null && networkException == null) {
          networkException = response.exception
        }
      }.map { response ->
        if (networkException != null) {
          response.newBuilder()
              .isLast(false)
              .build()
        } else {
          response
        }
      }

      emitAll(networkResponses)
      if (networkException == null) {
        return@flow
      }

      val cacheResponse = chain.proceed(
          request = request
              .newBuilder()
              .fetchFromCache(true)
              .build()
      ).single()
      emit(cacheResponse)
    }
  }
}

/**
 * An interceptor that emits the response from the cache first, and then emits the response(s) from the network.
 */
val CacheAndNetworkInterceptor = object : ApolloInterceptor {
  override fun  intercept(request: ApolloRequest, chain: ApolloInterceptorChain): Flow> {
    return flow {
      val cacheResponse = chain.proceed(
          request = request
              .newBuilder()
              .fetchFromCache(true)
              .build()
      ).single()

      emit(cacheResponse.newBuilder().isLast(false).build())

      val networkResponses = chain.proceed(request)
      emitAll(networkResponses)
    }
  }
}

internal object FetchPolicyRouterInterceptor : ApolloInterceptor, ApolloStoreInterceptor {
  override fun  intercept(request: ApolloRequest, chain: ApolloInterceptorChain): Flow> {
    if (request.operation !is Query) {
      // Subscriptions and Mutations do not support fetchPolicies
      return chain.proceed(request)
    }

    if (!request.conflateFetchPolicyInterceptorResponses) {
      // Fast path
      return request.fetchPolicyInterceptor.intercept(request, chain)
    }
    return flow {
      val exceptions = mutableListOf()
      var hasEmitted = false

      request.fetchPolicyInterceptor.intercept(request, chain)
          .collect {
            if (!hasEmitted && it.exception != null) {
              // Remember to send the exception later
              exceptions.add(it.exception!!)
              return@collect
            }
            emit(
                it.newBuilder()
                    .cacheInfo(
                        it.cacheInfo!!.newBuilder()
                            .cacheMissException(exceptions.filterIsInstance().firstOrNull())
                            .networkException(exceptions.firstOrNull { it !is CacheMissException })
                            .build()
                    )
                    .build()
            )
            hasEmitted = true
          }

      @Suppress("DEPRECATION")
      if (!hasEmitted) {
        // If we haven't emitted anything, send a composite exception
        val exception = when (exceptions.size) {
          0 -> DefaultApolloException("No response emitted")
          1 -> exceptions.first()
          2 -> ApolloCompositeException(exceptions.first(), exceptions.get(1))
          else -> ApolloCompositeException(exceptions.first(), exceptions.get(1)).apply {
            exceptions.drop(2).forEach {
              addSuppressed(it)
            }
          }
        }

        emit(
            ApolloResponse.Builder(request.operation, request.requestUuid)
                .exception(exception)
                .build()

        )
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy