Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear current ApolloStore related interceptors when calling .store() on builder #5857

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -13,6 +13,7 @@ import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory
import com.apollographql.apollo3.cache.normalized.api.Record
import com.apollographql.apollo3.cache.normalized.api.TypePolicyCacheKeyGenerator
import com.apollographql.apollo3.cache.normalized.internal.DefaultApolloStore
import com.apollographql.apollo3.interceptor.ApolloInterceptor
import com.benasher44.uuid.Uuid
import kotlinx.coroutines.flow.SharedFlow
import kotlin.reflect.KClass
Expand Down Expand Up @@ -265,3 +266,8 @@ fun ApolloStore(
cacheKeyGenerator: CacheKeyGenerator = TypePolicyCacheKeyGenerator,
cacheResolver: CacheResolver = FieldPolicyCacheResolver,
): ApolloStore = DefaultApolloStore(normalizedCacheFactory, cacheKeyGenerator, cacheResolver)

/**
* Interface that marks all interceptors added when configuring a `store()` on ApolloClient.Builder.
*/
internal interface ApolloStoreInterceptor : ApolloInterceptor
Expand Up @@ -122,6 +122,13 @@ fun ApolloClient.Builder.store(store: ApolloStore, writeToCacheAsynchronously: B
check(interceptors.none { it is AutoPersistedQueryInterceptor }) {
"Apollo: the normalized cache must be configured before the auto persisted queries"
}
// Removing existing interceptors added for configuring an [ApolloStore].
// If a builder is reused from an existing client using `newBuilder()` and we try to configure a new `store()` on it, we first need to
// remove the old interceptors.
val storeInterceptors = interceptors.filterIsInstance<ApolloStoreInterceptor>()
storeInterceptors.forEach {
removeInterceptor(it)
}
return addInterceptor(WatcherInterceptor(store))
.addInterceptor(FetchPolicyRouterInterceptor)
.addInterceptor(ApolloCacheInterceptor(store))
Expand Down
Expand Up @@ -129,7 +129,7 @@ val CacheAndNetworkInterceptor = object : ApolloInterceptor {
}
}

internal object FetchPolicyRouterInterceptor : ApolloInterceptor {
internal object FetchPolicyRouterInterceptor : ApolloInterceptor, ApolloStoreInterceptor {
override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
if (request.operation !is Query) {
// Subscriptions and Mutations do not support fetchPolicies
Expand Down
Expand Up @@ -9,6 +9,7 @@ import com.apollographql.apollo3.api.Operation
import com.apollographql.apollo3.api.Query
import com.apollographql.apollo3.api.Subscription
import com.apollographql.apollo3.cache.normalized.ApolloStore
import com.apollographql.apollo3.cache.normalized.ApolloStoreInterceptor
import com.apollographql.apollo3.cache.normalized.CacheInfo
import com.apollographql.apollo3.cache.normalized.api.ApolloCacheHeaders
import com.apollographql.apollo3.cache.normalized.api.CacheHeaders
Expand Down Expand Up @@ -36,7 +37,7 @@ import kotlinx.coroutines.launch

internal class ApolloCacheInterceptor(
val store: ApolloStore,
) : ApolloInterceptor {
) : ApolloInterceptor, ApolloStoreInterceptor {
private suspend fun <D : Operation.Data> maybeAsync(request: ApolloRequest<D>, block: suspend () -> Unit) {
if (request.writeToCacheAsynchronously) {
val scope = request.executionContext[ConcurrencyInfo]!!.coroutineScope
Expand Down
Expand Up @@ -6,6 +6,7 @@ import com.apollographql.apollo3.api.CustomScalarAdapters
import com.apollographql.apollo3.api.Operation
import com.apollographql.apollo3.api.Query
import com.apollographql.apollo3.cache.normalized.ApolloStore
import com.apollographql.apollo3.cache.normalized.ApolloStoreInterceptor
import com.apollographql.apollo3.cache.normalized.api.dependentKeys
import com.apollographql.apollo3.cache.normalized.watchContext
import com.apollographql.apollo3.exception.DefaultApolloException
Expand All @@ -23,7 +24,7 @@ import kotlinx.coroutines.flow.onSubscription

internal val WatcherSentinel = DefaultApolloException("The watcher has started")

internal class WatcherInterceptor(val store: ApolloStore) : ApolloInterceptor {
internal class WatcherInterceptor(val store: ApolloStore) : ApolloInterceptor, ApolloStoreInterceptor {
override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
val watchContext = request.watchContext ?: return chain.proceed(request)

Expand Down
1 change: 1 addition & 0 deletions libraries/apollo-runtime/api/android/apollo-runtime.api
Expand Up @@ -118,6 +118,7 @@ public final class com/apollographql/apollo3/ApolloClient$Builder : com/apollogr
public final fun httpServerUrl (Ljava/lang/String;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public final fun interceptors (Ljava/util/List;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public final fun networkTransport (Lcom/apollographql/apollo3/network/NetworkTransport;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public final fun removeInterceptor (Lcom/apollographql/apollo3/interceptor/ApolloInterceptor;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public fun sendApqExtensions (Ljava/lang/Boolean;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public synthetic fun sendApqExtensions (Ljava/lang/Boolean;)Ljava/lang/Object;
public fun sendDocument (Ljava/lang/Boolean;)Lcom/apollographql/apollo3/ApolloClient$Builder;
Expand Down
1 change: 1 addition & 0 deletions libraries/apollo-runtime/api/jvm/apollo-runtime.api
Expand Up @@ -118,6 +118,7 @@ public final class com/apollographql/apollo3/ApolloClient$Builder : com/apollogr
public final fun httpServerUrl (Ljava/lang/String;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public final fun interceptors (Ljava/util/List;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public final fun networkTransport (Lcom/apollographql/apollo3/network/NetworkTransport;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public final fun removeInterceptor (Lcom/apollographql/apollo3/interceptor/ApolloInterceptor;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public fun sendApqExtensions (Ljava/lang/Boolean;)Lcom/apollographql/apollo3/ApolloClient$Builder;
public synthetic fun sendApqExtensions (Ljava/lang/Boolean;)Ljava/lang/Object;
public fun sendDocument (Ljava/lang/Boolean;)Lcom/apollographql/apollo3/ApolloClient$Builder;
Expand Down
Expand Up @@ -776,6 +776,15 @@ private constructor(
_interceptors.add(interceptor)
}

/**
* Removes an [ApolloInterceptor] from this [ApolloClient].
*
* **The order is important**. This method removes the first occurrence of the [ApolloInterceptor] in the list.
*/
fun removeInterceptor(interceptor: ApolloInterceptor) = apply {
_interceptors.remove(interceptor)
}

/**
* Adds several [ApolloInterceptor]s to this [ApolloClient].
*
Expand Down
18 changes: 16 additions & 2 deletions tests/integration-tests/src/commonTest/kotlin/test/StoreTest.kt
Expand Up @@ -120,6 +120,17 @@ class StoreTest {
assertFriendIsNotCached("1003")
}

@Test
fun testNewBuilderNewStore() = runTest(before = { setUp() }) {
storeAllFriends()
assertFriendIsCached("1000", "Luke Skywalker")

val newStore = ApolloStore(MemoryCacheFactory())
val newClient = apolloClient.newBuilder().store(newStore).build()

assertFriendIsNotCached("1000", newClient)
}

private suspend fun storeAllFriends() {
val query = HeroAndFriendsNamesWithIDsQuery(Episode.NEWHOPE)
apolloClient.enqueueTestResponse(query, HeroAndFriendsNamesWithIDsQuery.Data(
Expand Down Expand Up @@ -158,9 +169,12 @@ class StoreTest {
assertEquals2(characterResponse.data?.character?.name, name)
}

private suspend fun assertFriendIsNotCached(id: String) {
private suspend fun assertFriendIsNotCached(
id: String,
apolloClientToUse: ApolloClient = apolloClient,
) {
assertIs<CacheMissException>(
apolloClient.query(CharacterNameByIdQuery(id))
apolloClientToUse.query(CharacterNameByIdQuery(id))
.fetchPolicy(FetchPolicy.CacheOnly)
.execute()
.exception
Expand Down