Skip to content

Commit 01b135b

Browse files
authored
Let isFromCache be about the ApolloResponse (apollographql#5805)
1 parent c016f84 commit 01b135b

File tree

4 files changed

+103
-3
lines changed

4 files changed

+103
-3
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Error handling changes in v4
2+
3+
This document describes the changes made around error handling in v4. The purpose is to keep a log of the changes, in a
4+
kind of "decision record" way.
5+
6+
## Broaden the meaning of `ApolloResponse.isFromCache`
7+
8+
In v3, `ApolloResponse.isFromCache` returns `true` if the **data** comes from the cache (implementation:
9+
`cacheInfo?.isCacheHit == true`). A more descriptive name could have been `isCacheHit`.
10+
11+
In v4, since cache misses no longer throw, a response can come from the cache whether the data is present or not.
12+
13+
With that in mind, it makes more sense for `isFromCache` to return `true` if the **response** comes from the cache,
14+
regardless of the data being present or not (new implementation:
15+
`cacheInfo?.isCacheHit == true || exception is CacheMissException`).
16+
17+
Note: this **is** a behavior change for projects that used `emitCacheMisses(true)` in v3:
18+
19+
- cache miss responses in v3: `isFromCache` would return `false` ("data is not a cache hit")
20+
- cache miss responses in v4: `isFromCache` will return `true` ("response is from the cache")
21+
22+
More context in [#5799](https://github.com/apollographql/apollo-kotlin/issues/5799).

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,15 @@ class CacheInfo private constructor(
552552
}
553553
}
554554

555+
/**
556+
* True if this response comes from the cache, false if it comes from the network.
557+
*
558+
* Note that this can be true regardless of whether the data was found in the cache.
559+
* To know whether the **data** is from the cache, use `cacheInfo?.isCacheHit == true`.
560+
*/
555561
val <D : Operation.Data> ApolloResponse<D>.isFromCache: Boolean
556562
get() {
557-
return cacheInfo?.isCacheHit == true
563+
return cacheInfo?.isCacheHit == true || exception is CacheMissException
558564
}
559565

560566
val <D : Operation.Data> ApolloResponse<D>.cacheInfo

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,9 +533,15 @@ class CacheInfo private constructor(
533533
}
534534
}
535535

536+
/**
537+
* True if this response comes from the cache, false if it comes from the network.
538+
*
539+
* Note that this can be true regardless of whether the data was found in the cache.
540+
* To know whether the **data** is from the cache, use `cacheInfo?.isCacheHit == true`.
541+
*/
536542
val <D : Operation.Data> ApolloResponse<D>.isFromCache: Boolean
537543
get() {
538-
return cacheInfo?.isCacheHit == true
544+
return cacheInfo?.isCacheHit == true || exception is CacheMissException
539545
}
540546

541547
val <D : Operation.Data> ApolloResponse<D>.cacheInfo
@@ -645,4 +651,4 @@ val <D : Operation.Data> ApolloResponse<D>.cacheHeaders
645651
*/
646652
@Deprecated("Use fetchPolicy(FetchPolicy.CacheAndNetwork) instead", ReplaceWith("fetchPolicy(FetchPolicy.CacheAndNetwork).toFlow()"), level = DeprecationLevel.ERROR)
647653
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v3_7_5)
648-
fun <D : Query.Data> ApolloCall<D>.executeCacheAndNetwork(): Flow<ApolloResponse<D>> = TODO()
654+
fun <D : Query.Data> ApolloCall<D>.executeCacheAndNetwork(): Flow<ApolloResponse<D>> = TODO()

tests/integration-tests/src/commonTest/kotlin/test/FetchPolicyTest.kt

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,4 +623,70 @@ class FetchPolicyTest {
623623
job.cancel()
624624
channel.cancel()
625625
}
626+
627+
@Test
628+
fun isFromCache() = runTest(before = { setUp() }, after = { tearDown() }) {
629+
val query = HeroNameQuery()
630+
val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2"))
631+
mockServer.enqueue(query, data)
632+
633+
// NetworkOnly / hit
634+
var response = apolloClient.query(query)
635+
.fetchPolicy(FetchPolicy.NetworkOnly)
636+
.execute()
637+
assertNotNull(response.data)
638+
assertFalse(response.isFromCache)
639+
640+
// CacheOnly / hit
641+
response = apolloClient.query(query)
642+
.fetchPolicy(FetchPolicy.CacheOnly)
643+
.execute()
644+
assertNotNull(response.data)
645+
assertTrue(response.isFromCache)
646+
647+
// CacheOnly / miss
648+
store.clearAll()
649+
response = apolloClient.query(query)
650+
.fetchPolicy(FetchPolicy.CacheOnly)
651+
.execute()
652+
assertNull(response.data)
653+
assertTrue(response.isFromCache)
654+
655+
// NetworkOnly / miss
656+
mockServer.enqueueString("malformed")
657+
response = apolloClient.query(query)
658+
.fetchPolicy(FetchPolicy.NetworkOnly)
659+
.execute()
660+
assertNull(response.data)
661+
assertFalse(response.isFromCache)
662+
663+
// CacheFirst / miss / miss
664+
store.clearAll()
665+
mockServer.enqueueString("malformed")
666+
var responses = apolloClient.query(query)
667+
.fetchPolicy(FetchPolicy.CacheFirst)
668+
.toFlow()
669+
.toList()
670+
assertTrue(responses[0].isFromCache)
671+
assertFalse(responses[1].isFromCache)
672+
673+
// NetworkFirst / miss / miss
674+
store.clearAll()
675+
mockServer.enqueueString("malformed")
676+
responses = apolloClient.query(query)
677+
.fetchPolicy(FetchPolicy.NetworkFirst)
678+
.toFlow()
679+
.toList()
680+
assertFalse(responses[0].isFromCache)
681+
assertTrue(responses[1].isFromCache)
682+
683+
// CacheAndNetwork / hit / hit
684+
mockServer.enqueue(query, data)
685+
responses = apolloClient.query(query)
686+
.fetchPolicy(FetchPolicy.CacheAndNetwork)
687+
.toFlow()
688+
.toList()
689+
assertTrue(responses[0].isFromCache)
690+
assertFalse(responses[1].isFromCache)
691+
}
626692
}

0 commit comments

Comments
 (0)