Skip to content

Commit d7803c6

Browse files
committed
Report watchInternal to incubating (apollographql#5875)
* Report watchInternal to incubating * Fix apiDump
1 parent 1b92dbd commit d7803c6

File tree

3 files changed

+52
-19
lines changed

3 files changed

+52
-19
lines changed

libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import com.apollographql.apollo3.cache.normalized.api.RecordMerger
3434
import com.apollographql.apollo3.cache.normalized.api.TypePolicyCacheKeyGenerator
3535
import com.apollographql.apollo3.cache.normalized.internal.ApolloCacheInterceptor
3636
import com.apollographql.apollo3.cache.normalized.internal.WatcherInterceptor
37+
import com.apollographql.apollo3.cache.normalized.internal.WatcherSentinel
3738
import com.apollographql.apollo3.exception.ApolloException
3839
import com.apollographql.apollo3.exception.CacheMissException
3940
import com.apollographql.apollo3.interceptor.ApolloInterceptor
@@ -42,9 +43,9 @@ import com.apollographql.apollo3.interceptor.AutoPersistedQueryInterceptor
4243
import com.apollographql.apollo3.mpp.currentTimeMillis
4344
import com.apollographql.apollo3.network.http.HttpInfo
4445
import kotlinx.coroutines.flow.Flow
46+
import kotlinx.coroutines.flow.filter
4547
import kotlinx.coroutines.flow.flow
4648
import kotlinx.coroutines.flow.map
47-
import kotlinx.coroutines.flow.onStart
4849
import kotlin.jvm.JvmName
4950
import kotlin.jvm.JvmOverloads
5051

@@ -152,10 +153,7 @@ fun ApolloClient.Builder.logCacheMisses(
152153
return addInterceptor(CacheMissLoggingInterceptor(log))
153154
}
154155

155-
fun ApolloClient.Builder.store(
156-
store: ApolloStore,
157-
writeToCacheAsynchronously: Boolean = false,
158-
): ApolloClient.Builder {
156+
fun ApolloClient.Builder.store(store: ApolloStore, writeToCacheAsynchronously: Boolean = false): ApolloClient.Builder {
159157
check(interceptors.none { it is AutoPersistedQueryInterceptor }) {
160158
"Apollo: the normalized cache must be configured before the auto persisted queries"
161159
}
@@ -188,8 +186,14 @@ fun <D : Query.Data> ApolloCall<D>.watch(
188186
): Flow<ApolloResponse<D>> = throw UnsupportedOperationException("watch(fetchThrows: Boolean, refetchThrows: Boolean) is no longer supported, use watch() instead")
189187

190188
/**
191-
* Gets the result from the network, then observes the cache for any changes.
192-
* [fetchPolicy] will control how the result is first queried, while [refetchPolicy] will control the subsequent fetches.
189+
* Gets initial response(s) then observes the cache for any changes.
190+
*
191+
* There is a guarantee that the cache is subscribed before the initial response(s) finish emitting. Any update to the cache done after the initial response(s) are received will be received.
192+
*
193+
* [fetchPolicy] controls how the result is first queried, while [refetchPolicy] will control the subsequent fetches.
194+
*
195+
* @see fetchPolicy
196+
* @see refetchPolicy
193197
*/
194198
fun <D : Query.Data> ApolloCall<D>.watch(): Flow<ApolloResponse<D>> {
195199
return flow {
@@ -223,14 +227,18 @@ fun <D : Query.Data> ApolloCall<D>.watch(): Flow<ApolloResponse<D>> {
223227
}
224228
}
225229

230+
226231
copy().fetchPolicyInterceptor(refetchPolicyInterceptor)
227-
.watch(response?.data)
228-
.onStart {
229-
if (lastResponse != null) {
230-
emit(lastResponse!!)
232+
.watchInternal(response?.data)
233+
.collect {
234+
if (it.exception === WatcherSentinel) {
235+
if (lastResponse != null) {
236+
emit(lastResponse!!)
237+
lastResponse = null
238+
}
239+
} else {
240+
emit(it)
231241
}
232-
}.collect {
233-
emit(it)
234242
}
235243
}
236244
}
@@ -240,6 +248,14 @@ fun <D : Query.Data> ApolloCall<D>.watch(): Flow<ApolloResponse<D>> {
240248
* The fetch policy set by [fetchPolicy] will be used.
241249
*/
242250
fun <D : Query.Data> ApolloCall<D>.watch(data: D?): Flow<ApolloResponse<D>> {
251+
return watchInternal(data).filter { it.exception !== WatcherSentinel }
252+
}
253+
254+
/**
255+
* Observes the cache for the given data. Unlike [watch], no initial request is executed on the network.
256+
* The fetch policy set by [fetchPolicy] will be used.
257+
*/
258+
internal fun <D : Query.Data> ApolloCall<D>.watchInternal(data: D?): Flow<ApolloResponse<D>> {
243259
return copy().addExecutionContext(WatchContext(data)).toFlow()
244260
}
245261

libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/WatcherInterceptor.kt

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,20 @@ import com.apollographql.apollo3.cache.normalized.ApolloStore
99
import com.apollographql.apollo3.cache.normalized.ApolloStoreInterceptor
1010
import com.apollographql.apollo3.cache.normalized.api.dependentKeys
1111
import com.apollographql.apollo3.cache.normalized.watchContext
12+
import com.apollographql.apollo3.exception.DefaultApolloException
1213
import com.apollographql.apollo3.interceptor.ApolloInterceptor
1314
import com.apollographql.apollo3.interceptor.ApolloInterceptorChain
1415
import kotlinx.coroutines.flow.Flow
16+
import kotlinx.coroutines.flow.SharedFlow
1517
import kotlinx.coroutines.flow.emitAll
1618
import kotlinx.coroutines.flow.filter
1719
import kotlinx.coroutines.flow.flow
20+
import kotlinx.coroutines.flow.flowOf
1821
import kotlinx.coroutines.flow.map
1922
import kotlinx.coroutines.flow.onEach
23+
import kotlinx.coroutines.flow.onSubscription
24+
25+
internal val WatcherSentinel = DefaultApolloException("The watcher has started")
2026

2127
internal class WatcherInterceptor(val store: ApolloStore) : ApolloInterceptor, ApolloStoreInterceptor {
2228
override fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>> {
@@ -31,16 +37,26 @@ internal class WatcherInterceptor(val store: ApolloStore) : ApolloInterceptor, A
3137
@Suppress("UNCHECKED_CAST")
3238
var watchedKeys: Set<String>? = watchContext.data?.let { store.normalize(request.operation, it as D, customScalarAdapters).values.dependentKeys() }
3339

34-
return store.changedKeys
40+
return (store.changedKeys as SharedFlow<Any>)
41+
.onSubscription {
42+
emit(Unit)
43+
}
3544
.filter { changedKeys ->
45+
if (changedKeys !is Set<*>) {
46+
return@filter true
47+
}
3648
watchedKeys == null || changedKeys.intersect(watchedKeys!!).isNotEmpty()
3749
}.map {
38-
chain.proceed(request.newBuilder().build())
39-
.onEach { response ->
40-
if (response.data != null) {
41-
watchedKeys = store.normalize(request.operation, response.data!!, customScalarAdapters).values.dependentKeys()
50+
if (it == Unit) {
51+
flowOf(ApolloResponse.Builder(request.operation, request.requestUuid).exception(WatcherSentinel).build())
52+
} else {
53+
chain.proceed(request)
54+
.onEach { response ->
55+
if (response.data != null) {
56+
watchedKeys = store.normalize(request.operation, response.data!!, customScalarAdapters).values.dependentKeys()
57+
}
4258
}
43-
}
59+
}
4460
}
4561
.flattenConcatPolyfill()
4662
}

libraries/apollo-runtime/api/apollo-runtime.klib.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ final class com.apollographql.apollo3/ApolloClient : com.apollographql.apollo3.a
306306
final fun httpServerUrl(kotlin/String?): com.apollographql.apollo3/ApolloClient.Builder // com.apollographql.apollo3/ApolloClient.Builder.httpServerUrl|httpServerUrl(kotlin.String?){}[0]
307307
final fun interceptors(kotlin.collections/List<com.apollographql.apollo3.interceptor/ApolloInterceptor>): com.apollographql.apollo3/ApolloClient.Builder // com.apollographql.apollo3/ApolloClient.Builder.interceptors|interceptors(kotlin.collections.List<com.apollographql.apollo3.interceptor.ApolloInterceptor>){}[0]
308308
final fun networkTransport(com.apollographql.apollo3.network/NetworkTransport?): com.apollographql.apollo3/ApolloClient.Builder // com.apollographql.apollo3/ApolloClient.Builder.networkTransport|networkTransport(com.apollographql.apollo3.network.NetworkTransport?){}[0]
309+
final fun removeHttpInterceptor(com.apollographql.apollo3.network.http/HttpInterceptor): com.apollographql.apollo3/ApolloClient.Builder // com.apollographql.apollo3/ApolloClient.Builder.removeHttpInterceptor|removeHttpInterceptor(com.apollographql.apollo3.network.http.HttpInterceptor){}[0]
309310
final fun removeInterceptor(com.apollographql.apollo3.interceptor/ApolloInterceptor): com.apollographql.apollo3/ApolloClient.Builder // com.apollographql.apollo3/ApolloClient.Builder.removeInterceptor|removeInterceptor(com.apollographql.apollo3.interceptor.ApolloInterceptor){}[0]
310311
final fun sendApqExtensions(kotlin/Boolean?): com.apollographql.apollo3/ApolloClient.Builder // com.apollographql.apollo3/ApolloClient.Builder.sendApqExtensions|sendApqExtensions(kotlin.Boolean?){}[0]
311312
final fun sendDocument(kotlin/Boolean?): com.apollographql.apollo3/ApolloClient.Builder // com.apollographql.apollo3/ApolloClient.Builder.sendDocument|sendDocument(kotlin.Boolean?){}[0]

0 commit comments

Comments
 (0)