Skip to content

Commit e89b5c3

Browse files
authored
Return existing cached values (e.g. Errors) in FieldPolicyCacheResolver (#198)
* Return existing cached values (e.g. Errors) in FieldPolicyCacheResolver * Also return existing cached values (e.g. Errors) in FieldPolicyCacheResolver in non-list cases * Add a test where items are fetched from 2 different lists. The error in 1st list cannot be seen from the 2nd one in the cache. * Fix merge shenanigans
1 parent c6f2fce commit e89b5c3

File tree

5 files changed

+462
-15
lines changed

5 files changed

+462
-15
lines changed

normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheResolver.kt

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@ package com.apollographql.cache.normalized.api
33
import com.apollographql.apollo.api.CompiledField
44
import com.apollographql.apollo.api.CompiledListType
55
import com.apollographql.apollo.api.CompiledNotNullType
6+
import com.apollographql.apollo.api.Error
67
import com.apollographql.apollo.api.Executable
78
import com.apollographql.apollo.api.MutableExecutionOptions
9+
import com.apollographql.apollo.api.json.MapJsonReader.Companion.buffer
10+
import com.apollographql.apollo.api.json.jsonReader
811
import com.apollographql.apollo.exception.CacheMissException
912
import com.apollographql.apollo.mpp.currentTimeMillis
1013
import com.apollographql.cache.normalized.api.CacheResolver.ResolvedValue
1114
import com.apollographql.cache.normalized.maxStale
1215
import com.apollographql.cache.normalized.storeExpirationDate
16+
import com.apollographql.cache.normalized.storeReceivedDate
17+
import okio.Buffer
1318
import kotlin.jvm.JvmSuppressWildcards
1419

1520
/**
@@ -255,7 +260,8 @@ fun FieldPolicyCacheResolver(
255260
keyScope: CacheKey.Scope = CacheKey.Scope.TYPE,
256261
) = object : CacheResolver {
257262
override fun resolveField(context: ResolverContext): Any? {
258-
val keyArgsValues = context.field.argumentValues(context.variables) { it.definition.isKey }.values
263+
val keyArgs = context.field.argumentValues(context.variables) { it.definition.isKey }
264+
val keyArgsValues = keyArgs.values
259265
if (keyArgsValues.isEmpty()) {
260266
return DefaultCacheResolver.resolveField(context)
261267
}
@@ -270,21 +276,70 @@ fun FieldPolicyCacheResolver(
270276
if (keyArgsValues.size == 1) {
271277
val keyArgsValue = keyArgsValues.first() as? List<*>
272278
if (keyArgsValue != null && keyArgsValue.firstOrNull() !is List<*>) {
273-
return keyArgsValue.map {
274-
if (keyScope == CacheKey.Scope.TYPE) {
275-
CacheKey(type.rawType().name, it.toString())
279+
val listItemsInParent: Map<Any?, Any?> = listItemsInParent(context, keyArgs.keys.first())
280+
return keyArgsValue.mapIndexed { index, value ->
281+
if (listItemsInParent.containsKey(value)) {
282+
listItemsInParent[value]?.let {
283+
if (it is Error) {
284+
it.withIndex(index)
285+
} else {
286+
it
287+
}
288+
}
276289
} else {
277-
CacheKey(it.toString())
290+
if (keyScope == CacheKey.Scope.TYPE) {
291+
CacheKey(type.rawType().name, value.toString())
292+
} else {
293+
CacheKey(value.toString())
294+
}
278295
}
279296
}
280297
}
281298
}
282299
}
283300
}
284-
return if (keyScope == CacheKey.Scope.TYPE) {
285-
CacheKey(type.rawType().name, keyArgsValues.map { it.toString() })
301+
val fieldKey = context.getFieldKey()
302+
return if (context.parent.containsKey(fieldKey)) {
303+
context.parent[fieldKey]
286304
} else {
287-
CacheKey(keyArgsValues.map { it.toString() })
305+
if (keyScope == CacheKey.Scope.TYPE) {
306+
CacheKey(type.rawType().name, keyArgsValues.map { it.toString() })
307+
} else {
308+
CacheKey(keyArgsValues.map { it.toString() })
309+
}
310+
}
311+
}
312+
313+
private fun Error.withIndex(index: Int): Error {
314+
val builder = Error.Builder(message)
315+
if (locations != null) builder.locations(locations!!)
316+
if (extensions != null) {
317+
for ((k, v) in extensions!!) {
318+
builder.putExtension(k, v)
319+
}
320+
}
321+
if (path != null) {
322+
builder.path(path!!.toMutableList().apply { this[this.lastIndex] = index })
323+
}
324+
return builder.build()
325+
}
326+
327+
private fun listItemsInParent(
328+
context: ResolverContext,
329+
keyArg: String,
330+
): Map<Any?, Any?> {
331+
val keyPrefix = "${context.field.name}("
332+
val filteredParent = context.parent.filterKeys { it.startsWith(keyPrefix) && it.contains("\"$keyArg\":") }
333+
val items: Map<Any?, Any?> = filteredParent.map { (k, v) ->
334+
val argumentsText = k.removePrefix(keyPrefix).removeSuffix(")")
335+
val argumentsMap = Buffer().writeUtf8(argumentsText).jsonReader().buffer().root as Map<*, *>
336+
val keyValues = argumentsMap[keyArg] as List<*>
337+
keyValues.mapIndexed { index, id ->
338+
id to (v as List<*>)[index]
339+
}.toMap()
340+
}.fold(emptyMap()) { acc, map ->
341+
acc + map
288342
}
343+
return items
289344
}
290345
}

tests/cache-options/src/commonMain/graphql/extra.graphqls

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ extend schema
55
)
66

77
extend type User @typePolicy(keyFields: "id")
8+
9+
extend type Query @fieldPolicy(forField: "users", keyArgs: "ids")
10+
extend type Query @fieldPolicy(forField: "user", keyArgs: "category")

tests/cache-options/src/commonMain/graphql/operation.graphql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ query UsersQuery($ids: [ID!]!) {
3030
}
3131
}
3232

33+
query AllUsersQuery {
34+
allUsers {
35+
id
36+
firstName
37+
lastName
38+
email
39+
}
40+
}
41+
42+
query UserByCategoryQuery($category: Category!) {
43+
user(category: $category) {
44+
id
45+
firstName
46+
lastName
47+
email
48+
}
49+
}
50+
3351
query MeWithBestFriendQuery {
3452
me {
3553
id

tests/cache-options/src/commonMain/graphql/schema.graphqls

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
type Query {
22
me: User!
33
users(ids: [ID!]!): [User]!
4+
allUsers: [User]!
45
project(id: ID! = "1"): Project
5-
user(category: Category!): User!
6+
user(category: Category!): User
67
someInt: Int
78
someInt2: Int
89
}

0 commit comments

Comments
 (0)