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

com.apollographql.apollo3.internal.RealApolloSubscriptionCall.kt Maven / Gradle / Ivy

package com.apollographql.apollo3.internal

import com.apollographql.apollo3.ApolloSubscriptionCall
import com.apollographql.apollo3.api.ApolloResponse
import com.apollographql.apollo3.api.CustomScalarAdapters
import com.apollographql.apollo3.api.Subscription
import com.apollographql.apollo3.api.internal.ApolloLogger
import com.apollographql.apollo3.cache.CacheHeaders
import com.apollographql.apollo3.cache.normalized.ApolloStore
import com.apollographql.apollo3.exception.ApolloCanceledException
import com.apollographql.apollo3.exception.ApolloNetworkException
import com.apollographql.apollo3.internal.CallState.IllegalStateMessage.Companion.forCurrentState
import com.apollographql.apollo3.internal.subscription.ApolloSubscriptionException
import com.apollographql.apollo3.internal.subscription.SubscriptionManager
import com.apollographql.apollo3.internal.subscription.SubscriptionResponse
import com.apollographql.apollo3.withCacheInfo
import com.benasher44.uuid.uuid4
import java.util.concurrent.Executor
import java.util.concurrent.atomic.AtomicReference
import kotlinx.coroutines.runBlocking

class RealApolloSubscriptionCall(
    private val subscription: Subscription,
    private val subscriptionManager: SubscriptionManager,
    private val apolloStore: ApolloStore,
    private val cachePolicy: ApolloSubscriptionCall.CachePolicy,
    private val dispatcher: Executor,
    private val logger: ApolloLogger,
    private val customScalarAdapters: CustomScalarAdapters
) : ApolloSubscriptionCall {
  private val state = AtomicReference(CallState.IDLE)
  private var subscriptionCallback: SubscriptionManagerCallback? = null

  @Throws(ApolloCanceledException::class)
  override fun execute(callback: ApolloSubscriptionCall.Callback) {
    synchronized(this) {
      when (state.get()) {
        CallState.IDLE -> {
          state.set(CallState.ACTIVE)
          if (cachePolicy == ApolloSubscriptionCall.CachePolicy.CACHE_AND_NETWORK) {
            dispatcher.execute {
              val cachedResponse = resolveFromCache()
              if (cachedResponse != null) {
                callback.onResponse(cachedResponse)
              }
            }
          }
          subscriptionCallback = SubscriptionManagerCallback(callback, this)
          subscriptionManager.subscribe(subscription, subscriptionCallback!!)
        }
        CallState.CANCELED -> throw ApolloCanceledException()
        CallState.TERMINATED, CallState.ACTIVE -> throw IllegalStateException("Already Executed")
        else -> throw IllegalStateException("Unknown state")
      }
    }
  }

  override fun cancel() {
    synchronized(this) {
      when (state.get()) {
        CallState.IDLE -> {
          state.set(CallState.CANCELED)
        }
        CallState.ACTIVE -> {
          try {
            subscriptionManager.unsubscribe(subscription)
          } finally {
            state.set(CallState.CANCELED)
            subscriptionCallback!!.release()
          }
        }
        CallState.CANCELED, CallState.TERMINATED -> {
        }
        else -> throw IllegalStateException("Unknown state")
      }
    }
  }

  override fun clone(): ApolloSubscriptionCall {
    return RealApolloSubscriptionCall(subscription, subscriptionManager, apolloStore, cachePolicy, dispatcher, logger, customScalarAdapters)
  }

  override val isCanceled: Boolean
    get() = state.get() === CallState.CANCELED

  override fun cachePolicy(cachePolicy: ApolloSubscriptionCall.CachePolicy): ApolloSubscriptionCall {
    return RealApolloSubscriptionCall(
        subscription = subscription,
        subscriptionManager = subscriptionManager,
        apolloStore = apolloStore,
        cachePolicy = cachePolicy,
        dispatcher = dispatcher,
        logger = logger,
        customScalarAdapters = customScalarAdapters
    )
  }

  private fun terminate() {
    synchronized(this) {
      when (state.get()) {
        CallState.ACTIVE -> {
          state.set(CallState.TERMINATED)
          subscriptionCallback!!.release()
        }
        CallState.CANCELED -> {
        }
        CallState.IDLE, CallState.TERMINATED -> throw IllegalStateException(
            forCurrentState(state.get()).expected(CallState.ACTIVE, CallState.CANCELED))
        else -> throw IllegalStateException("Unknown state")
      }
    }
  }

  private fun resolveFromCache(): ApolloResponse? {
    val data = runBlocking {
      apolloStore.readOperation(
          subscription,
          customScalarAdapters,
          CacheHeaders.NONE,
      )
    }
    return if (data != null) {
      logger.d("Cache HIT for subscription `%s`", subscription)
      ApolloResponse(
          requestUuid = uuid4(),
          operation = subscription,
          data = data,
      ).withCacheInfo(true)
    } else {
      logger.d("Cache MISS for subscription `%s`", subscription)
      null
    }
  }

  private class SubscriptionManagerCallback(private var originalCallback: ApolloSubscriptionCall.Callback?, private var delegate: RealApolloSubscriptionCall?) : SubscriptionManager.Callback {
    override fun onResponse(response: SubscriptionResponse) {
      val callback = originalCallback
      val data = response.response.data
      if (callback != null) {
        if (data != null && delegate!!.cachePolicy != ApolloSubscriptionCall.CachePolicy.NO_CACHE) {
          runBlocking {
            delegate!!.apolloStore.writeOperation(
                operation = response.subscription,
                operationData = data,
                customScalarAdapters = delegate!!.customScalarAdapters,
                cacheHeaders = CacheHeaders.NONE,
                publish = true,
            )
          }
        }
        callback.onResponse(response.response)
      }
    }

    override fun onError(error: ApolloSubscriptionException) {
      val callback = originalCallback
      callback?.onFailure(error)
      terminate()
    }

    override fun onNetworkError(t: Throwable) {
      val callback = originalCallback
      callback?.onFailure(ApolloNetworkException("Subscription failed", t))
      terminate()
    }

    override fun onCompleted() {
      val callback = originalCallback
      callback?.onCompleted()
      terminate()
    }

    override fun onTerminated() {
      val callback = originalCallback
      callback?.onTerminated()
      terminate()
    }

    override fun onConnected() {
      val callback = originalCallback
      callback?.onConnected()
    }

    fun terminate() {
      val delegate = delegate
      delegate?.terminate()
    }

    fun release() {
      originalCallback = null
      delegate = null
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy