diff --git a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ApolloStore.kt b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ApolloStore.kt index a94937349c2..e753cc72400 100644 --- a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ApolloStore.kt +++ b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ApolloStore.kt @@ -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 @@ -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 diff --git a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt index 0e9729ff924..b84f541c2c8 100644 --- a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt +++ b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt @@ -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() + storeInterceptors.forEach { + removeInterceptor(it) + } return addInterceptor(WatcherInterceptor(store)) .addInterceptor(FetchPolicyRouterInterceptor) .addInterceptor(ApolloCacheInterceptor(store)) diff --git a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/FetchPolicyInterceptors.kt b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/FetchPolicyInterceptors.kt index f94ba23994a..94ac5e6808e 100644 --- a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/FetchPolicyInterceptors.kt +++ b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/FetchPolicyInterceptors.kt @@ -129,7 +129,7 @@ val CacheAndNetworkInterceptor = object : ApolloInterceptor { } } -internal object FetchPolicyRouterInterceptor : ApolloInterceptor { +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 diff --git a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/ApolloCacheInterceptor.kt b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/ApolloCacheInterceptor.kt index 0e14a50acd5..c6e0727863f 100644 --- a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/ApolloCacheInterceptor.kt +++ b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/ApolloCacheInterceptor.kt @@ -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 @@ -36,7 +37,7 @@ import kotlinx.coroutines.launch internal class ApolloCacheInterceptor( val store: ApolloStore, -) : ApolloInterceptor { +) : ApolloInterceptor, ApolloStoreInterceptor { private suspend fun maybeAsync(request: ApolloRequest, block: suspend () -> Unit) { if (request.writeToCacheAsynchronously) { val scope = request.executionContext[ConcurrencyInfo]!!.coroutineScope diff --git a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/WatcherInterceptor.kt b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/WatcherInterceptor.kt index 654eb5651c9..f2f0a3dd714 100644 --- a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/WatcherInterceptor.kt +++ b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/WatcherInterceptor.kt @@ -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 @@ -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 intercept(request: ApolloRequest, chain: ApolloInterceptorChain): Flow> { val watchContext = request.watchContext ?: return chain.proceed(request) diff --git a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/ApolloClient.kt b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/ApolloClient.kt index d85e4fe4c7e..dc4f60e55ba 100644 --- a/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/ApolloClient.kt +++ b/libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo3/ApolloClient.kt @@ -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]. * diff --git a/tests/integration-tests/src/commonTest/kotlin/test/StoreTest.kt b/tests/integration-tests/src/commonTest/kotlin/test/StoreTest.kt index 89d1317851c..8695cc0c693 100644 --- a/tests/integration-tests/src/commonTest/kotlin/test/StoreTest.kt +++ b/tests/integration-tests/src/commonTest/kotlin/test/StoreTest.kt @@ -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( @@ -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( - apolloClient.query(CharacterNameByIdQuery(id)) + apolloClientToUse.query(CharacterNameByIdQuery(id)) .fetchPolicy(FetchPolicy.CacheOnly) .execute() .exception