diff --git a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/ApolloCacheCompilerPlugin.kt b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/ApolloCacheCompilerPlugin.kt index 53b0096d..7ec7282c 100644 --- a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/ApolloCacheCompilerPlugin.kt +++ b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/ApolloCacheCompilerPlugin.kt @@ -13,7 +13,7 @@ internal class ApolloCacheCompilerPlugin( private val environment: ApolloCompilerPluginEnvironment, ) : ApolloCompilerPlugin { override fun foreignSchemas(): List { - return listOf(ForeignSchema("cache", "v0.1", cacheControlGQLDefinitions)) + return listOf(ForeignSchema("cache", "v0.1", cacheGQLDefinitions)) } override fun schemaListener(): SchemaListener { diff --git a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/CacheSchemaListener.kt b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/CacheSchemaListener.kt index feef71fb..01da16b3 100644 --- a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/CacheSchemaListener.kt +++ b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/CacheSchemaListener.kt @@ -14,10 +14,10 @@ import com.squareup.kotlinpoet.MAP import com.squareup.kotlinpoet.MemberName import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.PropertySpec -import com.squareup.kotlinpoet.SET import com.squareup.kotlinpoet.STRING import com.squareup.kotlinpoet.TypeSpec import com.squareup.kotlinpoet.asTypeName +import com.squareup.kotlinpoet.withIndent import java.io.File import kotlin.time.Duration @@ -26,6 +26,7 @@ private object Symbols { val MaxAgeInherit = MaxAge.nestedClass("Inherit") val MaxAgeDuration = MaxAge.nestedClass("Duration") val Seconds = MemberName(Duration.Companion::class.asTypeName(), "seconds", isExtension = true) + val TypePolicy = ClassName("com.apollographql.cache.normalized.api", "TypePolicy") } internal class CacheSchemaListener( @@ -34,13 +35,11 @@ internal class CacheSchemaListener( override fun onSchema(schema: Schema, outputDirectory: File) { val packageName = (environment.arguments["packageName"] as? String ?: throw IllegalArgumentException("packageName argument is required and must be a String")) + ".cache" - val maxAgeProperty = maxAgeProperty(schema) - val keyFieldsProperty = keyFieldsProperty(schema) val file = FileSpec.builder(packageName, "Cache") .addType( TypeSpec.objectBuilder("Cache") - .addProperty(maxAgeProperty) - .addProperty(keyFieldsProperty) + .addProperty(maxAgeProperty(schema)) + .addProperty(typePoliciesProperty(schema)) .build() ) .addFileComment( @@ -53,7 +52,6 @@ internal class CacheSchemaListener( """.trimIndent() ) .build() - file.writeTo(outputDirectory) } @@ -61,48 +59,53 @@ internal class CacheSchemaListener( val maxAges = schema.getMaxAges(environment.logger) val initializer = CodeBlock.builder().apply { add("mapOf(\n") - indent() - maxAges.forEach { (field, duration) -> - if (duration == -1) { - addStatement("%S to %T,", field, Symbols.MaxAgeInherit) - } else { - addStatement("%S to %T(%L.%M),", field, Symbols.MaxAgeDuration, duration, Symbols.Seconds) + withIndent { + maxAges.forEach { (field, duration) -> + if (duration == -1) { + addStatement("%S to %T,", field, Symbols.MaxAgeInherit) + } else { + addStatement("%S to %T(%L.%M),", field, Symbols.MaxAgeDuration, duration, Symbols.Seconds) + } } } - unindent() add(")") } .build() - return PropertySpec.Companion.builder("maxAges", MAP - .parameterizedBy(STRING, Symbols.MaxAge) + return PropertySpec.Companion.builder( + name = "maxAges", + type = MAP.parameterizedBy(STRING, Symbols.MaxAge) ) .initializer(initializer) .build() } - private fun keyFieldsProperty(schema: Schema): PropertySpec { - val keyFields = schema.getObjectKeyFields() + private fun typePoliciesProperty(schema: Schema): PropertySpec { + val typePolicies = schema.getTypePolicies() val initializer = CodeBlock.builder().apply { add("mapOf(\n") - indent() - keyFields.forEach { (type, fields) -> - addStatement("%S to setOf(", type) - indent() - fields.forEach { field -> - addStatement("%S,", field) + withIndent { + typePolicies.forEach { (type, typePolicy) -> + addStatement("%S to %T(", type, Symbols.TypePolicy) + withIndent { + addStatement("keyFields = setOf(") + withIndent { + typePolicy.keyFields.forEach { keyField -> + addStatement("%S, ", keyField) + } + } + add("),\n") + } + addStatement("),") } - unindent() - addStatement("),") } - unindent() add(")") } .build() - return PropertySpec.builder("keyFields", MAP - .parameterizedBy(STRING, SET.parameterizedBy(STRING)) + return PropertySpec.builder( + name = "typePolicies", + type = MAP.parameterizedBy(STRING, Symbols.TypePolicy) ) .initializer(initializer) .build() } } - diff --git a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/cacheControlDefinitions.kt b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/cacheDefinitions.kt similarity index 91% rename from normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/cacheControlDefinitions.kt rename to normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/cacheDefinitions.kt index f9a1784e..bccc9cf6 100644 --- a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/cacheControlDefinitions.kt +++ b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/cacheDefinitions.kt @@ -2,7 +2,7 @@ package com.apollographql.cache.apollocompilerplugin.internal import com.apollographql.apollo.ast.parseAsGQLDocument -private val cacheControlDefinitions_0_1 = """ +private val cacheDefinitions_0_1 = """ ""${'"'} Possible values for the `@cacheControl` `scope` argument (unused on the client). ""${'"'} @@ -64,4 +64,4 @@ private val cacheControlDefinitions_0_1 = """ ) repeatable on OBJECT | INTERFACE """.trimIndent() -internal val cacheControlGQLDefinitions = cacheControlDefinitions_0_1.parseAsGQLDocument().getOrThrow().definitions +internal val cacheGQLDefinitions = cacheDefinitions_0_1.parseAsGQLDocument().getOrThrow().definitions diff --git a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/getObjectKeyFields.kt b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/getObjectKeyFields.kt deleted file mode 100644 index 1518e353..00000000 --- a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/getObjectKeyFields.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.apollographql.cache.apollocompilerplugin.internal - -import com.apollographql.apollo.ast.GQLDirective -import com.apollographql.apollo.ast.GQLField -import com.apollographql.apollo.ast.GQLInterfaceTypeDefinition -import com.apollographql.apollo.ast.GQLObjectTypeDefinition -import com.apollographql.apollo.ast.GQLStringValue -import com.apollographql.apollo.ast.GQLTypeDefinition -import com.apollographql.apollo.ast.Schema -import com.apollographql.apollo.ast.Schema.Companion.TYPE_POLICY -import com.apollographql.apollo.ast.SourceAwareException -import com.apollographql.apollo.ast.parseAsGQLSelections - -/** - * Returns the key fields for each object type in the schema. - */ -internal fun Schema.getObjectKeyFields(): Map> { - val keyFieldsCache = mutableMapOf>() - return typeDefinitions.values - .filter { it is GQLObjectTypeDefinition } - .associate { - it.name to validateAndComputeKeyFields(it, keyFieldsCache) - } - .filterValues { it.isNotEmpty() } -} - -/** - * Returns the key fields for this type definition. - * - * If an interface defines key fields, its subtypes inherit those key fields. It is an error trying to redefine the key fields in a subtype. - */ -private fun Schema.validateAndComputeKeyFields( - typeDefinition: GQLTypeDefinition, - keyFieldsCache: MutableMap>, -): Set { - val cached = keyFieldsCache[typeDefinition.name] - if (cached != null) { - return cached - } - - val (directives, interfaces) = when (typeDefinition) { - is GQLObjectTypeDefinition -> typeDefinition.directives to typeDefinition.implementsInterfaces - is GQLInterfaceTypeDefinition -> typeDefinition.directives to typeDefinition.implementsInterfaces - else -> error("Cannot get directives for $typeDefinition") - } - - val interfacesKeyFields = interfaces.map { validateAndComputeKeyFields(typeDefinitions[it]!!, keyFieldsCache) } - .filter { it.isNotEmpty() } - val distinct = interfacesKeyFields.distinct() - if (distinct.size > 1) { - val extra = interfaces.indices.joinToString("\n") { - "${interfaces[it]}: ${interfacesKeyFields[it]}" - } - throw SourceAwareException( - error = "Apollo: Type '${typeDefinition.name}' cannot inherit different keys from different interfaces:\n$extra", - sourceLocation = typeDefinition.sourceLocation - ) - } - - val keyFields = directives.filter { originalDirectiveName(it.name) == TYPE_POLICY }.toKeyFields() - val ret = if (keyFields.isNotEmpty()) { - if (distinct.isNotEmpty()) { - val extra = interfaces.indices.joinToString("\n") { - "${interfaces[it]}: ${interfacesKeyFields[it]}" - } - throw SourceAwareException( - error = "Type '${typeDefinition.name}' cannot have key fields since it implements the following interfaces which also have key fields: $extra", - sourceLocation = typeDefinition.sourceLocation - ) - } - keyFields - } else { - distinct.firstOrNull() ?: emptySet() - } - - keyFieldsCache[typeDefinition.name] = ret - - return ret -} - -private fun List.toKeyFields(): Set = extractFields("keyFields") - -private fun List.extractFields(argumentName: String): Set { - if (isEmpty()) { - return emptySet() - } - return flatMap { - val value = it.arguments.firstOrNull { - it.name == argumentName - }?.value - - val selectionSet = (value as? GQLStringValue)?.value ?: return@flatMap emptyList() - - selectionSet.parseAsGQLSelections().getOrThrow().map { gqlSelection -> - // No need to check here, this should be done during validation - (gqlSelection as GQLField).name - } - }.toSet() -} diff --git a/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/getTypePolicies.kt b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/getTypePolicies.kt new file mode 100644 index 00000000..1468b173 --- /dev/null +++ b/normalized-cache-apollo-compiler-plugin/src/main/kotlin/com/apollographql/cache/apollocompilerplugin/internal/getTypePolicies.kt @@ -0,0 +1,92 @@ +package com.apollographql.cache.apollocompilerplugin.internal + +import com.apollographql.apollo.ast.GQLDirective +import com.apollographql.apollo.ast.GQLField +import com.apollographql.apollo.ast.GQLInterfaceTypeDefinition +import com.apollographql.apollo.ast.GQLObjectTypeDefinition +import com.apollographql.apollo.ast.GQLStringValue +import com.apollographql.apollo.ast.GQLTypeDefinition +import com.apollographql.apollo.ast.Schema +import com.apollographql.apollo.ast.Schema.Companion.TYPE_POLICY +import com.apollographql.apollo.ast.SourceAwareException +import com.apollographql.apollo.ast.parseAsGQLSelections + +internal data class TypePolicy( + val keyFields: Set, +) + +/** + * Returns the type policies for object types and interfaces in the schema. + */ +internal fun Schema.getTypePolicies(): Map { + val typePolicyCache = mutableMapOf() + @Suppress("UNCHECKED_CAST") + return typeDefinitions.values + .filter { it is GQLObjectTypeDefinition || it is GQLInterfaceTypeDefinition } + .associate { + it.name to validateAndComputeTypePolicy(it, typePolicyCache) + } + .filterValues { it != null } as Map +} + +/** + * Returns the type policy for this type definition. + * + * If an interface defines a type policy, its subtypes inherit that type policy. It is an error trying to redefine the type policy in a subtype. + */ +private fun Schema.validateAndComputeTypePolicy( + typeDefinition: GQLTypeDefinition, + typePolicyCache: MutableMap, +): TypePolicy? { + if (typePolicyCache.contains(typeDefinition.name)) { + return typePolicyCache[typeDefinition.name] + } + val (directives, interfaces) = when (typeDefinition) { + is GQLObjectTypeDefinition -> typeDefinition.directives to typeDefinition.implementsInterfaces + is GQLInterfaceTypeDefinition -> typeDefinition.directives to typeDefinition.implementsInterfaces + else -> error("Unexpected $typeDefinition") + } + + val interfacesTypePolicy = interfaces.mapNotNull { validateAndComputeTypePolicy(typeDefinitions[it]!!, typePolicyCache) } + val distinct = interfacesTypePolicy.distinct() + if (distinct.size > 1) { + val extra = interfaces.indices.joinToString("\n") { + "${interfaces[it]}: ${interfacesTypePolicy[it]}" + } + throw SourceAwareException( + error = "Apollo: Type '${typeDefinition.name}' cannot inherit different @typePolicy from different interfaces:\n$extra", + sourceLocation = typeDefinition.sourceLocation + ) + } + + val typePolicyDirective = directives.firstOrNull { originalDirectiveName(it.name) == TYPE_POLICY } + val typePolicy = typePolicyDirective?.toTypePolicy() + val ret = if (typePolicy != null) { + if (distinct.isNotEmpty()) { + val extra = interfaces.indices.joinToString("\n") { + "${interfaces[it]}: ${interfacesTypePolicy[it]}" + } + throw SourceAwareException( + error = "Apollo: Type '${typeDefinition.name}' cannot have @typePolicy since it implements the following interfaces which also have @typePolicy: $extra", + sourceLocation = typeDefinition.sourceLocation + ) + } + typePolicy + } else { + distinct.firstOrNull() + } + typePolicyCache[typeDefinition.name] = ret + return ret +} + +private fun GQLDirective.toTypePolicy(): TypePolicy { + val keyFields = ((arguments.single { it.name == "keyFields" }.value as GQLStringValue).value + .parseAsGQLSelections().value?.map { gqlSelection -> + (gqlSelection as GQLField).name + } ?: throw SourceAwareException("Apollo: keyArgs should be a selectionSet", sourceLocation)) + .toSet() + + return TypePolicy( + keyFields = keyFields, + ) +} diff --git a/normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/GetMaxAgesTest.kt b/normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/GetMaxAgesTest.kt index d2dbe911..93858946 100644 --- a/normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/GetMaxAgesTest.kt +++ b/normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/GetMaxAgesTest.kt @@ -65,7 +65,7 @@ class GetMaxAgesTest { .validateAsSchema( SchemaValidationOptions( addKotlinLabsDefinitions = true, - foreignSchemas = builtinForeignSchemas() + ForeignSchema("cache", "v0.1", cacheControlGQLDefinitions) + foreignSchemas = builtinForeignSchemas() + ForeignSchema("cache", "v0.1", cacheGQLDefinitions) ) ) .getOrThrow() @@ -147,7 +147,7 @@ class GetMaxAgesTest { .validateAsSchema( SchemaValidationOptions( addKotlinLabsDefinitions = true, - foreignSchemas = builtinForeignSchemas() + ForeignSchema("cache", "v0.1", cacheControlGQLDefinitions) + foreignSchemas = builtinForeignSchemas() + ForeignSchema("cache", "v0.1", cacheGQLDefinitions) ) ) .getOrThrow() diff --git a/normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/ValidateAndComputeKeyFieldsTest.kt b/normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/GetTypePoliciesTest.kt similarity index 82% rename from normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/ValidateAndComputeKeyFieldsTest.kt rename to normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/GetTypePoliciesTest.kt index ab5cb883..ffd80ef8 100644 --- a/normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/ValidateAndComputeKeyFieldsTest.kt +++ b/normalized-cache-apollo-compiler-plugin/src/test/kotlin/com/apollographql/cache/apollocompilerplugin/internal/GetTypePoliciesTest.kt @@ -3,6 +3,7 @@ package com.apollographql.cache.apollocompilerplugin.internal import com.apollographql.apollo.annotations.ApolloExperimental +import com.apollographql.apollo.ast.ForeignSchema import com.apollographql.apollo.ast.SourceAwareException import com.apollographql.apollo.ast.internal.SchemaValidationOptions import com.apollographql.apollo.ast.parseAsGQLDocument @@ -12,9 +13,9 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith -class ValidateAndComputeKeyFieldsTest { +class GetTypePoliciesTest { @Test - fun keyFieldsOnObject() { + fun simpleTypePolicies() { // language=GraphQL val schema = """ type Query { @@ -58,13 +59,15 @@ class ValidateAndComputeKeyFieldsTest { ).getOrThrow() val expected = mapOf( - "User" to setOf("id"), - "Lion" to setOf("kingdom", "species"), - "Circle" to setOf("id"), - "Square" to setOf("radius"), + "User" to TypePolicy(keyFields = setOf("id")), + "Animal" to TypePolicy(keyFields = setOf("kingdom", "species")), + "Lion" to TypePolicy(keyFields = setOf("kingdom", "species")), + "HasId" to TypePolicy(keyFields = setOf("id")), + "Circle" to TypePolicy(keyFields = setOf("id")), + "Square" to TypePolicy(keyFields = setOf("radius")), ) - assertEquals(expected, schema.getObjectKeyFields()) + assertEquals(expected, schema.getTypePolicies()) } // TODO: Ignored because the same checks are already done in validateAsSchema in v4. @@ -97,12 +100,12 @@ class ValidateAndComputeKeyFieldsTest { .validateAsSchema( SchemaValidationOptions( addKotlinLabsDefinitions = true, - foreignSchemas = emptyList() + foreignSchemas = listOf(ForeignSchema("cache", "v0.1", cacheGQLDefinitions)) ) ).getOrThrow() assertFailsWith { - schema.getObjectKeyFields() + schema.getTypePolicies() }.apply { assertEquals(""" e: null: (14, 1): Apollo: Type 'Lion' cannot inherit different keys from different interfaces: @@ -139,12 +142,12 @@ class ValidateAndComputeKeyFieldsTest { .validateAsSchema( SchemaValidationOptions( addKotlinLabsDefinitions = true, - foreignSchemas = emptyList() + foreignSchemas = listOf(ForeignSchema("cache", "v0.1", cacheGQLDefinitions)) ) ).getOrThrow() assertFailsWith { - schema.getObjectKeyFields() + schema.getTypePolicies() }.apply { assertEquals(""" e: null: (10, 1): Type 'Lion' cannot have key fields since it implements the following interfaces which also have key fields: Animal: [kingdom, species] diff --git a/normalized-cache/api/normalized-cache.api b/normalized-cache/api/normalized-cache.api index 26f43e79..9b9b5551 100644 --- a/normalized-cache/api/normalized-cache.api +++ b/normalized-cache/api/normalized-cache.api @@ -274,6 +274,7 @@ public final class com/apollographql/cache/normalized/api/CacheKey { public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; public static fun constructor-impl (Ljava/lang/String;Ljava/util/List;)Ljava/lang/String; public static fun constructor-impl (Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/String; + public static fun constructor-impl (Ljava/util/List;)Ljava/lang/String; public fun equals (Ljava/lang/Object;)Z public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z @@ -291,6 +292,14 @@ public final class com/apollographql/cache/normalized/api/CacheKey$Companion { public final fun getSUBSCRIPTION_ROOT-mqw0cJ0 ()Ljava/lang/String; } +public final class com/apollographql/cache/normalized/api/CacheKey$Scope : java/lang/Enum { + public static final field SERVICE Lcom/apollographql/cache/normalized/api/CacheKey$Scope; + public static final field TYPE Lcom/apollographql/cache/normalized/api/CacheKey$Scope; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/cache/normalized/api/CacheKey$Scope; + public static fun values ()[Lcom/apollographql/cache/normalized/api/CacheKey$Scope; +} + public abstract interface class com/apollographql/cache/normalized/api/CacheKeyGenerator { public abstract fun cacheKeyForObject-z2_y8R0 (Ljava/util/Map;Lcom/apollographql/cache/normalized/api/CacheKeyGeneratorContext;)Ljava/lang/String; } @@ -301,6 +310,11 @@ public final class com/apollographql/cache/normalized/api/CacheKeyGeneratorConte public final fun getVariables ()Lcom/apollographql/apollo/api/Executable$Variables; } +public final class com/apollographql/cache/normalized/api/CacheKeyGeneratorKt { + public static final fun TypePolicyCacheKeyGenerator (Ljava/util/Map;Lcom/apollographql/cache/normalized/api/CacheKey$Scope;)Lcom/apollographql/cache/normalized/api/CacheKeyGenerator; + public static synthetic fun TypePolicyCacheKeyGenerator$default (Ljava/util/Map;Lcom/apollographql/cache/normalized/api/CacheKey$Scope;ILjava/lang/Object;)Lcom/apollographql/cache/normalized/api/CacheKeyGenerator; +} + public abstract class com/apollographql/cache/normalized/api/CacheKeyResolver : com/apollographql/cache/normalized/api/CacheResolver { public fun ()V public abstract fun cacheKeyForField-fLoEQYY (Lcom/apollographql/cache/normalized/api/ResolverContext;)Ljava/lang/String; @@ -319,6 +333,8 @@ public final class com/apollographql/cache/normalized/api/CacheResolver$Resolved } public final class com/apollographql/cache/normalized/api/CacheResolverKt { + public static final fun FieldPolicyCacheResolver (Lcom/apollographql/cache/normalized/api/CacheKey$Scope;)Lcom/apollographql/cache/normalized/api/CacheResolver; + public static synthetic fun FieldPolicyCacheResolver$default (Lcom/apollographql/cache/normalized/api/CacheKey$Scope;ILjava/lang/Object;)Lcom/apollographql/cache/normalized/api/CacheResolver; public static final fun getFieldKey (Lcom/apollographql/cache/normalized/api/ResolverContext;)Ljava/lang/String; } @@ -429,24 +445,19 @@ public final class com/apollographql/cache/normalized/api/GlobalMaxAgeProvider : public final class com/apollographql/cache/normalized/api/IdCacheKeyGenerator : com/apollographql/cache/normalized/api/CacheKeyGenerator { public fun ()V - public fun ([Ljava/lang/String;)V - public synthetic fun ([Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun ([Ljava/lang/String;Lcom/apollographql/cache/normalized/api/CacheKey$Scope;)V + public synthetic fun ([Ljava/lang/String;Lcom/apollographql/cache/normalized/api/CacheKey$Scope;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun cacheKeyForObject-z2_y8R0 (Ljava/util/Map;Lcom/apollographql/cache/normalized/api/CacheKeyGeneratorContext;)Ljava/lang/String; } public final class com/apollographql/cache/normalized/api/IdCacheKeyResolver : com/apollographql/cache/normalized/api/CacheKeyResolver { public fun ()V - public fun (Ljava/util/List;Ljava/util/List;)V - public synthetic fun (Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/util/List;Ljava/util/List;Lcom/apollographql/cache/normalized/api/CacheKey$Scope;)V + public synthetic fun (Ljava/util/List;Ljava/util/List;Lcom/apollographql/cache/normalized/api/CacheKey$Scope;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun cacheKeyForField-fLoEQYY (Lcom/apollographql/cache/normalized/api/ResolverContext;)Ljava/lang/String; public fun listOfCacheKeysForField (Lcom/apollographql/cache/normalized/api/ResolverContext;)Ljava/util/List; } -public final class com/apollographql/cache/normalized/api/KeyFieldsCacheKeyGenerator : com/apollographql/cache/normalized/api/CacheKeyGenerator { - public fun (Ljava/util/Map;)V - public fun cacheKeyForObject-z2_y8R0 (Ljava/util/Map;Lcom/apollographql/cache/normalized/api/CacheKeyGeneratorContext;)Ljava/lang/String; -} - public abstract interface class com/apollographql/cache/normalized/api/MaxAge { } @@ -623,6 +634,11 @@ public final class com/apollographql/cache/normalized/api/SchemaCoordinatesMaxAg public fun getMaxAge-5sfh64U (Lcom/apollographql/cache/normalized/api/MaxAgeContext;)J } +public final class com/apollographql/cache/normalized/api/TypePolicy { + public fun (Ljava/util/Set;)V + public final fun getKeyFields ()Ljava/util/Set; +} + public final class com/apollographql/cache/normalized/api/TypePolicyCacheKeyGenerator : com/apollographql/cache/normalized/api/CacheKeyGenerator { public static final field INSTANCE Lcom/apollographql/cache/normalized/api/TypePolicyCacheKeyGenerator; public fun cacheKeyForObject-z2_y8R0 (Ljava/util/Map;Lcom/apollographql/cache/normalized/api/CacheKeyGeneratorContext;)Ljava/lang/String; diff --git a/normalized-cache/api/normalized-cache.klib.api b/normalized-cache/api/normalized-cache.klib.api index cd3b1eec..34125822 100644 --- a/normalized-cache/api/normalized-cache.klib.api +++ b/normalized-cache/api/normalized-cache.klib.api @@ -253,24 +253,18 @@ final class com.apollographql.cache.normalized.api/GlobalMaxAgeProvider : com.ap } final class com.apollographql.cache.normalized.api/IdCacheKeyGenerator : com.apollographql.cache.normalized.api/CacheKeyGenerator { // com.apollographql.cache.normalized.api/IdCacheKeyGenerator|null[0] - constructor (kotlin/Array... = ...) // com.apollographql.cache.normalized.api/IdCacheKeyGenerator.|(kotlin.Array...){}[0] + constructor (kotlin/Array... = ..., com.apollographql.cache.normalized.api/CacheKey.Scope = ...) // com.apollographql.cache.normalized.api/IdCacheKeyGenerator.|(kotlin.Array...;com.apollographql.cache.normalized.api.CacheKey.Scope){}[0] final fun cacheKeyForObject(kotlin.collections/Map, com.apollographql.cache.normalized.api/CacheKeyGeneratorContext): com.apollographql.cache.normalized.api/CacheKey? // com.apollographql.cache.normalized.api/IdCacheKeyGenerator.cacheKeyForObject|cacheKeyForObject(kotlin.collections.Map;com.apollographql.cache.normalized.api.CacheKeyGeneratorContext){}[0] } final class com.apollographql.cache.normalized.api/IdCacheKeyResolver : com.apollographql.cache.normalized.api/CacheKeyResolver { // com.apollographql.cache.normalized.api/IdCacheKeyResolver|null[0] - constructor (kotlin.collections/List = ..., kotlin.collections/List = ...) // com.apollographql.cache.normalized.api/IdCacheKeyResolver.|(kotlin.collections.List;kotlin.collections.List){}[0] + constructor (kotlin.collections/List = ..., kotlin.collections/List = ..., com.apollographql.cache.normalized.api/CacheKey.Scope = ...) // com.apollographql.cache.normalized.api/IdCacheKeyResolver.|(kotlin.collections.List;kotlin.collections.List;com.apollographql.cache.normalized.api.CacheKey.Scope){}[0] final fun cacheKeyForField(com.apollographql.cache.normalized.api/ResolverContext): com.apollographql.cache.normalized.api/CacheKey? // com.apollographql.cache.normalized.api/IdCacheKeyResolver.cacheKeyForField|cacheKeyForField(com.apollographql.cache.normalized.api.ResolverContext){}[0] final fun listOfCacheKeysForField(com.apollographql.cache.normalized.api/ResolverContext): kotlin.collections/List? // com.apollographql.cache.normalized.api/IdCacheKeyResolver.listOfCacheKeysForField|listOfCacheKeysForField(com.apollographql.cache.normalized.api.ResolverContext){}[0] } -final class com.apollographql.cache.normalized.api/KeyFieldsCacheKeyGenerator : com.apollographql.cache.normalized.api/CacheKeyGenerator { // com.apollographql.cache.normalized.api/KeyFieldsCacheKeyGenerator|null[0] - constructor (kotlin.collections/Map>) // com.apollographql.cache.normalized.api/KeyFieldsCacheKeyGenerator.|(kotlin.collections.Map>){}[0] - - final fun cacheKeyForObject(kotlin.collections/Map, com.apollographql.cache.normalized.api/CacheKeyGeneratorContext): com.apollographql.cache.normalized.api/CacheKey? // com.apollographql.cache.normalized.api/KeyFieldsCacheKeyGenerator.cacheKeyForObject|cacheKeyForObject(kotlin.collections.Map;com.apollographql.cache.normalized.api.CacheKeyGeneratorContext){}[0] -} - final class com.apollographql.cache.normalized.api/MaxAgeContext { // com.apollographql.cache.normalized.api/MaxAgeContext|null[0] constructor (kotlin.collections/List) // com.apollographql.cache.normalized.api/MaxAgeContext.|(kotlin.collections.List){}[0] @@ -384,6 +378,13 @@ final class com.apollographql.cache.normalized.api/SchemaCoordinatesMaxAgeProvid final fun getMaxAge(com.apollographql.cache.normalized.api/MaxAgeContext): kotlin.time/Duration // com.apollographql.cache.normalized.api/SchemaCoordinatesMaxAgeProvider.getMaxAge|getMaxAge(com.apollographql.cache.normalized.api.MaxAgeContext){}[0] } +final class com.apollographql.cache.normalized.api/TypePolicy { // com.apollographql.cache.normalized.api/TypePolicy|null[0] + constructor (kotlin.collections/Set) // com.apollographql.cache.normalized.api/TypePolicy.|(kotlin.collections.Set){}[0] + + final val keyFields // com.apollographql.cache.normalized.api/TypePolicy.keyFields|{}keyFields[0] + final fun (): kotlin.collections/Set // com.apollographql.cache.normalized.api/TypePolicy.keyFields.|(){}[0] +} + final class com.apollographql.cache.normalized.memory/MemoryCache : com.apollographql.cache.normalized.api/NormalizedCache { // com.apollographql.cache.normalized.memory/MemoryCache|null[0] constructor (com.apollographql.cache.normalized.api/NormalizedCache? = ..., kotlin/Int = ..., kotlin/Long = ...) // com.apollographql.cache.normalized.memory/MemoryCache.|(com.apollographql.cache.normalized.api.NormalizedCache?;kotlin.Int;kotlin.Long){}[0] @@ -506,6 +507,7 @@ final class com.apollographql.cache.normalized/RemovedFieldsAndRecords { // com. } final value class com.apollographql.cache.normalized.api/CacheKey { // com.apollographql.cache.normalized.api/CacheKey|null[0] + constructor (kotlin.collections/List) // com.apollographql.cache.normalized.api/CacheKey.|(kotlin.collections.List){}[0] constructor (kotlin/String) // com.apollographql.cache.normalized.api/CacheKey.|(kotlin.String){}[0] constructor (kotlin/String, kotlin.collections/List) // com.apollographql.cache.normalized.api/CacheKey.|(kotlin.String;kotlin.collections.List){}[0] constructor (kotlin/String, kotlin/Array...) // com.apollographql.cache.normalized.api/CacheKey.|(kotlin.String;kotlin.Array...){}[0] @@ -517,6 +519,17 @@ final value class com.apollographql.cache.normalized.api/CacheKey { // com.apoll final fun hashCode(): kotlin/Int // com.apollographql.cache.normalized.api/CacheKey.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // com.apollographql.cache.normalized.api/CacheKey.toString|toString(){}[0] + final enum class Scope : kotlin/Enum { // com.apollographql.cache.normalized.api/CacheKey.Scope|null[0] + enum entry SERVICE // com.apollographql.cache.normalized.api/CacheKey.Scope.SERVICE|null[0] + enum entry TYPE // com.apollographql.cache.normalized.api/CacheKey.Scope.TYPE|null[0] + + final val entries // com.apollographql.cache.normalized.api/CacheKey.Scope.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // com.apollographql.cache.normalized.api/CacheKey.Scope.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): com.apollographql.cache.normalized.api/CacheKey.Scope // com.apollographql.cache.normalized.api/CacheKey.Scope.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // com.apollographql.cache.normalized.api/CacheKey.Scope.values|values#static(){}[0] + } + final object Companion { // com.apollographql.cache.normalized.api/CacheKey.Companion|null[0] final val MUTATION_ROOT // com.apollographql.cache.normalized.api/CacheKey.Companion.MUTATION_ROOT|{}MUTATION_ROOT[0] final fun (): com.apollographql.cache.normalized.api/CacheKey // com.apollographql.cache.normalized.api/CacheKey.Companion.MUTATION_ROOT.|(){}[0] @@ -648,4 +661,6 @@ final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOption final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/storePartialResponses(kotlin/Boolean): kotlin/Nothing // com.apollographql.cache.normalized/storePartialResponses|storePartialResponses@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§}[0] final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/storeReceivedDate(kotlin/Boolean): #A // com.apollographql.cache.normalized/storeReceivedDate|storeReceivedDate@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§}[0] final fun <#A: kotlin/Any?> (com.apollographql.apollo.api/MutableExecutionOptions<#A>).com.apollographql.cache.normalized/writeToCacheAsynchronously(kotlin/Boolean): #A // com.apollographql.cache.normalized/writeToCacheAsynchronously|writeToCacheAsynchronously@com.apollographql.apollo.api.MutableExecutionOptions<0:0>(kotlin.Boolean){0§}[0] +final fun com.apollographql.cache.normalized.api/FieldPolicyCacheResolver(com.apollographql.cache.normalized.api/CacheKey.Scope = ...): com.apollographql.cache.normalized.api/CacheResolver // com.apollographql.cache.normalized.api/FieldPolicyCacheResolver|FieldPolicyCacheResolver(com.apollographql.cache.normalized.api.CacheKey.Scope){}[0] +final fun com.apollographql.cache.normalized.api/TypePolicyCacheKeyGenerator(kotlin.collections/Map, com.apollographql.cache.normalized.api/CacheKey.Scope = ...): com.apollographql.cache.normalized.api/CacheKeyGenerator // com.apollographql.cache.normalized.api/TypePolicyCacheKeyGenerator|TypePolicyCacheKeyGenerator(kotlin.collections.Map;com.apollographql.cache.normalized.api.CacheKey.Scope){}[0] final fun com.apollographql.cache.normalized/CacheManager(com.apollographql.cache.normalized.api/NormalizedCacheFactory, com.apollographql.cache.normalized.api/CacheKeyGenerator = ..., com.apollographql.cache.normalized.api/MetadataGenerator = ..., com.apollographql.cache.normalized.api/CacheResolver = ..., com.apollographql.cache.normalized.api/RecordMerger = ..., com.apollographql.cache.normalized.api/FieldKeyGenerator = ..., com.apollographql.cache.normalized.api/EmbeddedFieldsProvider = ..., com.apollographql.cache.normalized.api/MaxAgeProvider = ...): com.apollographql.cache.normalized/CacheManager // com.apollographql.cache.normalized/CacheManager|CacheManager(com.apollographql.cache.normalized.api.NormalizedCacheFactory;com.apollographql.cache.normalized.api.CacheKeyGenerator;com.apollographql.cache.normalized.api.MetadataGenerator;com.apollographql.cache.normalized.api.CacheResolver;com.apollographql.cache.normalized.api.RecordMerger;com.apollographql.cache.normalized.api.FieldKeyGenerator;com.apollographql.cache.normalized.api.EmbeddedFieldsProvider;com.apollographql.cache.normalized.api.MaxAgeProvider){}[0] diff --git a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/CacheManager.kt b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/CacheManager.kt index 0b56b262..83a68250 100644 --- a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/CacheManager.kt +++ b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/CacheManager.kt @@ -20,7 +20,6 @@ import com.apollographql.cache.normalized.api.DefaultRecordMerger import com.apollographql.cache.normalized.api.EmbeddedFieldsProvider import com.apollographql.cache.normalized.api.EmptyMetadataGenerator import com.apollographql.cache.normalized.api.FieldKeyGenerator -import com.apollographql.cache.normalized.api.FieldPolicyCacheResolver import com.apollographql.cache.normalized.api.MaxAgeProvider import com.apollographql.cache.normalized.api.MetadataGenerator import com.apollographql.cache.normalized.api.NormalizedCache @@ -329,7 +328,7 @@ fun CacheManager( @Suppress("DEPRECATION") cacheKeyGenerator: CacheKeyGenerator = com.apollographql.cache.normalized.api.TypePolicyCacheKeyGenerator, metadataGenerator: MetadataGenerator = EmptyMetadataGenerator, - cacheResolver: CacheResolver = FieldPolicyCacheResolver, + cacheResolver: CacheResolver = com.apollographql.cache.normalized.api.FieldPolicyCacheResolver(keyScope = CacheKey.Scope.TYPE), recordMerger: RecordMerger = DefaultRecordMerger, fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, embeddedFieldsProvider: EmbeddedFieldsProvider = DefaultEmbeddedFieldsProvider, diff --git a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/ClientCacheExtensions.kt b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/ClientCacheExtensions.kt index c5997477..1d33c431 100644 --- a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/ClientCacheExtensions.kt +++ b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/ClientCacheExtensions.kt @@ -22,6 +22,7 @@ import com.apollographql.apollo.mpp.currentTimeMillis import com.apollographql.apollo.network.http.HttpInfo import com.apollographql.cache.normalized.api.ApolloCacheHeaders import com.apollographql.cache.normalized.api.CacheHeaders +import com.apollographql.cache.normalized.api.CacheKey import com.apollographql.cache.normalized.api.CacheKeyGenerator import com.apollographql.cache.normalized.api.CacheResolver import com.apollographql.cache.normalized.api.DefaultEmbeddedFieldsProvider @@ -31,7 +32,6 @@ import com.apollographql.cache.normalized.api.DefaultRecordMerger import com.apollographql.cache.normalized.api.EmbeddedFieldsProvider import com.apollographql.cache.normalized.api.EmptyMetadataGenerator import com.apollographql.cache.normalized.api.FieldKeyGenerator -import com.apollographql.cache.normalized.api.FieldPolicyCacheResolver import com.apollographql.cache.normalized.api.MaxAgeProvider import com.apollographql.cache.normalized.api.MetadataGenerator import com.apollographql.cache.normalized.api.NormalizedCacheFactory @@ -67,7 +67,7 @@ fun ApolloClient.Builder.normalizedCache( @Suppress("DEPRECATION") cacheKeyGenerator: CacheKeyGenerator = com.apollographql.cache.normalized.api.TypePolicyCacheKeyGenerator, metadataGenerator: MetadataGenerator = EmptyMetadataGenerator, - cacheResolver: CacheResolver = FieldPolicyCacheResolver, + cacheResolver: CacheResolver = com.apollographql.cache.normalized.api.FieldPolicyCacheResolver(keyScope = CacheKey.Scope.TYPE), recordMerger: RecordMerger = DefaultRecordMerger, fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, embeddedFieldsProvider: EmbeddedFieldsProvider = DefaultEmbeddedFieldsProvider, diff --git a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKey.kt b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKey.kt index 3622da9f..21355e32 100644 --- a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKey.kt +++ b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKey.kt @@ -36,6 +36,10 @@ value class CacheKey( */ constructor(typename: String, vararg values: String) : this(typename, values.toList()) + constructor(values: List) : this( + values.joinToString("+") { it.replace("\\", "\\\\").replace("+", "\\+") } + ) + internal fun keyToString(): String { return key } @@ -47,6 +51,11 @@ value class CacheKey( val MUTATION_ROOT = CacheKey("MUTATION_ROOT") val SUBSCRIPTION_ROOT = CacheKey("SUBSCRIPTION_ROOT") } + + enum class Scope { + TYPE, + SERVICE, + } } internal fun CacheKey.isRootKey(): Boolean { diff --git a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKeyGenerator.kt b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKeyGenerator.kt index 1af7d160..3525aceb 100644 --- a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKeyGenerator.kt +++ b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKeyGenerator.kt @@ -41,15 +41,16 @@ class CacheKeyGeneratorContext( ) /** - * A [CacheKeyGenerator] that uses the `@typePolicy` directive to compute the id. + * A [CacheKeyGenerator] that uses the `@typePolicy` directive to compute the cache key. * * Note: this uses the key fields of the **schema** type and therefore can't generate cache keys for: * - unions * - interfaces that have a `@typePolicy` on subtypes. * - * For those cases, prefer [KeyFieldsCacheKeyGenerator], which uses the concrete type (`__typename`) instead. + * For those cases, prefer `fun TypePolicyCacheKeyGenerator(typePolicies, keyScope)`, which uses the concrete type (found in `__typename`) + * instead. */ -@Deprecated("Use KeyFieldsCacheKeyGenerator instead") +@Deprecated("Use TypePolicyCacheKeyGenerator(typePolicies, keyScope) instead") object TypePolicyCacheKeyGenerator : CacheKeyGenerator { override fun cacheKeyForObject(obj: Map, context: CacheKeyGeneratorContext): CacheKey? { val keyFields = context.field.type.rawType().keyFields() @@ -62,18 +63,29 @@ object TypePolicyCacheKeyGenerator : CacheKeyGenerator { } /** - * A [CacheKeyGenerator] that uses the given key fields to compute the cache key. + * A [CacheKeyGenerator] that uses the `@typePolicy` directive to compute the cache key. * - * This uses the field's concrete type (`__typename`). + * This uses the key fields of the field's concrete type (found in `__typename`). + * + * @param typePolicies the type policies as declared in the schema via `@typePolicy`. + * @param keyScope the scope of the generated cache keys. Use [CacheKey.Scope.TYPE] to namespace the keys by the concrete type name, or + * [CacheKey.Scope.SERVICE] if the ids are unique across the whole service. */ -class KeyFieldsCacheKeyGenerator(private val keyFields: Map>) : CacheKeyGenerator { +fun TypePolicyCacheKeyGenerator( + typePolicies: Map, + keyScope: CacheKey.Scope = CacheKey.Scope.TYPE, +) = object : CacheKeyGenerator { override fun cacheKeyForObject(obj: Map, context: CacheKeyGeneratorContext): CacheKey? { - val typename = obj["__typename"].toString() - val keyFields = keyFields[typename] - // If a type is unknown at build type, it might be an interface that has key fields - ?: context.field.type.rawType().keyFields().ifEmpty { null } + val typeName = obj["__typename"].toString() + val typePolicy = typePolicies[typeName] + // If a type is unknown at build type, it might be an interface that has a type policy + ?: typePolicies[context.field.type.rawType().name] ?: return null - return CacheKey(typename, keyFields.map { obj[it].toString() }) + return if (keyScope == CacheKey.Scope.TYPE) { + CacheKey(typeName, typePolicy.keyFields.map { obj[it].toString() }) + } else { + CacheKey(typePolicy.keyFields.map { obj[it].toString() }) + } } } @@ -81,14 +93,24 @@ class KeyFieldsCacheKeyGenerator(private val keyFields: Map> * A [CacheKeyGenerator] that uses the given id fields to compute the cache key. * If the id field(s) is/are missing, the object is considered to not have an id. * + * @param idFields the possible names of the fields to use as id. The first present one is used. + * @param keyScope the scope of the generated cache keys. Use [CacheKey.Scope.TYPE] to namespace the keys by the concrete type name, or + * [CacheKey.Scope.SERVICE] if the ids are unique across the whole service. + * * @see IdCacheKeyResolver */ -class IdCacheKeyGenerator(private vararg val idFields: String = arrayOf("id")) : CacheKeyGenerator { +class IdCacheKeyGenerator( + private vararg val idFields: String = arrayOf("id"), + private val keyScope: CacheKey.Scope = CacheKey.Scope.TYPE, +) : CacheKeyGenerator { override fun cacheKeyForObject(obj: Map, context: CacheKeyGeneratorContext): CacheKey? { val values = idFields.map { (obj[it] ?: return null).toString() } - val typeName = context.field.type.rawType().name - return CacheKey(typeName, values) + return if (keyScope == CacheKey.Scope.TYPE) { + CacheKey(obj["__typename"].toString(), values) + } else { + CacheKey(values) + } } } diff --git a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKeyResolver.kt b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKeyResolver.kt index ebb9cbd7..e004b463 100644 --- a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKeyResolver.kt +++ b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheKeyResolver.kt @@ -75,21 +75,27 @@ abstract class CacheKeyResolver : CacheResolver { * * @param idFields possible names of the argument containing the id for objects * @param idListFields possible names of the argument containing the ids for lists + * @param keyScope the scope of the computed cache keys. Use [CacheKey.Scope.TYPE] to namespace the keys by the schema type name, or + * [CacheKey.Scope.SERVICE] if the ids are unique across the whole service. * * @see IdCacheKeyGenerator */ class IdCacheKeyResolver( private val idFields: List = listOf("id"), private val idListFields: List = listOf("ids"), + private val keyScope: CacheKey.Scope = CacheKey.Scope.TYPE, ) : CacheKeyResolver() { override fun cacheKeyForField(context: ResolverContext): CacheKey? { val fieldKey = context.getFieldKey() if (context.parent[fieldKey] != null) { return null } - val typeName = context.field.type.rawType().name val id = idFields.firstNotNullOfOrNull { context.field.argumentValue(it, context.variables).getOrNull()?.toString() } ?: return null - return CacheKey(typeName, id) + return if (keyScope == CacheKey.Scope.TYPE) { + CacheKey(context.field.type.rawType().name, id) + } else { + CacheKey(id) + } } override fun listOfCacheKeysForField(context: ResolverContext): List? { @@ -97,9 +103,16 @@ class IdCacheKeyResolver( if (context.parent[fieldKey] != null) { return null } - val typeName = context.field.type.rawType().name val ids = idListFields.firstNotNullOfOrNull { context.field.argumentValue(it, context.variables).getOrNull() as? List<*> } ?: return null - return ids.map { id -> id?.toString()?.let { CacheKey(typeName, it) } } + return ids.map { id -> + id?.toString()?.let { + if (keyScope == CacheKey.Scope.TYPE) { + CacheKey(context.field.type.rawType().name, it) + } else { + CacheKey(it) + } + } + } } } diff --git a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheResolver.kt b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheResolver.kt index 9032bdde..37a56bcf 100644 --- a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheResolver.kt +++ b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/CacheResolver.kt @@ -161,13 +161,13 @@ object DefaultCacheResolver : CacheResolver { */ class CacheControlCacheResolver( private val maxAgeProvider: MaxAgeProvider, - private val delegateResolver: CacheResolver = FieldPolicyCacheResolver, + private val delegateResolver: CacheResolver = FieldPolicyCacheResolver(keyScope = CacheKey.Scope.TYPE), ) : CacheResolver { /** * Creates a new [CacheControlCacheResolver] with no max ages. Use this constructor if you want to consider only the expiration dates. */ constructor( - delegateResolver: CacheResolver = FieldPolicyCacheResolver, + delegateResolver: CacheResolver = FieldPolicyCacheResolver(keyScope = CacheKey.Scope.TYPE), ) : this( maxAgeProvider = DefaultMaxAgeProvider, delegateResolver = delegateResolver, @@ -228,9 +228,34 @@ class CacheControlCacheResolver( } /** - * A cache resolver that uses `@fieldPolicy` directives to resolve fields and delegates to [DefaultCacheResolver] otherwise + * A cache resolver that uses `@fieldPolicy` directives to resolve fields and delegates to [DefaultCacheResolver] otherwise. + * + * Note: this namespaces the ids with the **schema** type, will lead to cache misses for: + * - unions + * - interfaces that have a `@typePolicy` on subtypes. + * + * If the ids are unique across the whole service, use `FieldPolicyCacheResolver(keyScope = CacheKey.Scope.SERVICE)`. Otherwise there is + * no way to resolve the cache key automatically for those cases. + */ +@Deprecated("Use FieldPolicyCacheResolver(keyScope) instead") +object FieldPolicyCacheResolver : CacheResolver by FieldPolicyCacheResolver(keyScope = CacheKey.Scope.TYPE) + +/** + * A cache resolver that uses `@fieldPolicy` directives to resolve fields and delegates to [DefaultCacheResolver] otherwise. + * + * Note: using a [CacheKey.Scope.TYPE] `keyScope` namespaces the ids with the **schema** type, which will lead to cache misses for: + * - unions + * - interfaces that have a `@typePolicy` on subtypes. + * + * If the ids are unique across the whole service, use [CacheKey.Scope.SERVICE]. Otherwise there is no way to resolve the cache keys + * automatically for those cases. + * + * @param keyScope the scope of the computed cache keys. Use [CacheKey.Scope.TYPE] to namespace the keys by the schema type name, or + * [CacheKey.Scope.SERVICE] if the ids are unique across the whole service. */ -object FieldPolicyCacheResolver : CacheResolver { +fun FieldPolicyCacheResolver( + keyScope: CacheKey.Scope = CacheKey.Scope.TYPE, +) = object : CacheResolver { override fun resolveField(context: ResolverContext): Any? { val keyArgsValues = context.field.argumentValues(context.variables) { it.definition.isKey }.values if (keyArgsValues.isEmpty()) { @@ -248,12 +273,20 @@ object FieldPolicyCacheResolver : CacheResolver { val keyArgsValue = keyArgsValues.first() as? List<*> if (keyArgsValue != null && keyArgsValue.firstOrNull() !is List<*>) { return keyArgsValue.map { - CacheKey(type.rawType().name, it.toString()) + if (keyScope == CacheKey.Scope.TYPE) { + CacheKey(type.rawType().name, it.toString()) + } else { + CacheKey(it.toString()) + } } } } } } - return CacheKey(type.rawType().name, keyArgsValues.map { it.toString() }) + return if (keyScope == CacheKey.Scope.TYPE) { + CacheKey(type.rawType().name, keyArgsValues.map { it.toString() }) + } else { + CacheKey(keyArgsValues.map { it.toString() }) + } } } diff --git a/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/TypePolicy.kt b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/TypePolicy.kt new file mode 100644 index 00000000..4ea9cce2 --- /dev/null +++ b/normalized-cache/src/commonMain/kotlin/com/apollographql/cache/normalized/api/TypePolicy.kt @@ -0,0 +1,5 @@ +package com.apollographql.cache.normalized.api + +class TypePolicy( + val keyFields: Set, +) diff --git a/tests/models-operation-based-with-interfaces/src/commonTest/kotlin/test/StoreTest.kt b/tests/models-operation-based-with-interfaces/src/commonTest/kotlin/test/StoreTest.kt index 24cdff90..33ae1ea6 100644 --- a/tests/models-operation-based-with-interfaces/src/commonTest/kotlin/test/StoreTest.kt +++ b/tests/models-operation-based-with-interfaces/src/commonTest/kotlin/test/StoreTest.kt @@ -44,7 +44,7 @@ class StoreTest { val heroWithFriendsFragment = cacheManager.readFragment( HeroWithFriendsFragmentImpl(), - CacheKey("Character:2001"), + CacheKey("Droid:2001"), ).data assertEquals(heroWithFriendsFragment.id, "2001") assertEquals(heroWithFriendsFragment.name, "R2-D2") @@ -58,7 +58,7 @@ class StoreTest { var fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1000"), + CacheKey("Human:1000"), ).data assertEquals(fragment.id, "1000") @@ -66,14 +66,14 @@ class StoreTest { fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1002"), + CacheKey("Human:1002"), ).data assertEquals(fragment.id, "1002") assertEquals(fragment.name, "Han Solo") fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1003"), + CacheKey("Human:1003"), ).data assertEquals(fragment.id, "1003") assertEquals(fragment.name, "Leia Organa") @@ -100,7 +100,7 @@ class StoreTest { cacheManager.writeFragment( HeroWithFriendsFragmentImpl(), - CacheKey("Character:2001"), + CacheKey("Droid:2001"), HeroWithFriendsFragment( "2001", "R222-D222", @@ -125,7 +125,7 @@ class StoreTest { cacheManager.writeFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1002"), + CacheKey("Human:1002"), HumanWithIdFragment( "1002", "Beast" diff --git a/tests/models-operation-based/src/commonTest/kotlin/test/StoreTest.kt b/tests/models-operation-based/src/commonTest/kotlin/test/StoreTest.kt index 6f5997ef..6e4b8278 100644 --- a/tests/models-operation-based/src/commonTest/kotlin/test/StoreTest.kt +++ b/tests/models-operation-based/src/commonTest/kotlin/test/StoreTest.kt @@ -44,7 +44,7 @@ class StoreTest { val heroWithFriendsFragment = cacheManager.readFragment( HeroWithFriendsFragmentImpl(), - CacheKey("Character:2001"), + CacheKey("Droid:2001"), ).data assertEquals(heroWithFriendsFragment.id, "2001") assertEquals(heroWithFriendsFragment.name, "R2-D2") @@ -58,7 +58,7 @@ class StoreTest { var fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1000"), + CacheKey("Human:1000"), ).data assertEquals(fragment.id, "1000") @@ -66,14 +66,14 @@ class StoreTest { fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1002"), + CacheKey("Human:1002"), ).data assertEquals(fragment.id, "1002") assertEquals(fragment.name, "Han Solo") fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1003"), + CacheKey("Human:1003"), ).data assertEquals(fragment.id, "1003") assertEquals(fragment.name, "Leia Organa") @@ -100,7 +100,7 @@ class StoreTest { cacheManager.writeFragment( HeroWithFriendsFragmentImpl(), - CacheKey("Character:2001"), + CacheKey("Droid:2001"), HeroWithFriendsFragment( "2001", "R222-D222", @@ -125,7 +125,7 @@ class StoreTest { cacheManager.writeFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1002"), + CacheKey("Human:1002"), HumanWithIdFragment( "1002", "Beast" diff --git a/tests/models-response-based/src/commonTest/kotlin/test/StoreTest.kt b/tests/models-response-based/src/commonTest/kotlin/test/StoreTest.kt index ff444562..75f9285a 100644 --- a/tests/models-response-based/src/commonTest/kotlin/test/StoreTest.kt +++ b/tests/models-response-based/src/commonTest/kotlin/test/StoreTest.kt @@ -45,7 +45,7 @@ class StoreTest { val heroWithFriendsFragment = cacheManager.readFragment( HeroWithFriendsFragmentImpl(), - CacheKey("Character:2001"), + CacheKey("Droid:2001"), ).data assertEquals(heroWithFriendsFragment.id, "2001") assertEquals(heroWithFriendsFragment.name, "R2-D2") @@ -59,7 +59,7 @@ class StoreTest { var fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1000"), + CacheKey("Human:1000"), ).data assertEquals(fragment.id, "1000") @@ -67,14 +67,14 @@ class StoreTest { fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1002"), + CacheKey("Human:1002"), ).data assertEquals(fragment.id, "1002") assertEquals(fragment.name, "Han Solo") fragment = cacheManager.readFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1003"), + CacheKey("Human:1003"), ).data assertEquals(fragment.id, "1003") assertEquals(fragment.name, "Leia Organa") @@ -101,7 +101,7 @@ class StoreTest { cacheManager.writeFragment( HeroWithFriendsFragmentImpl(), - CacheKey("Character:2001"), + CacheKey("Droid:2001"), HeroWithFriendsFragmentImpl.Data( id = "2001", name = "R222-D222", @@ -122,7 +122,7 @@ class StoreTest { cacheManager.writeFragment( HumanWithIdFragmentImpl(), - CacheKey("Character:1002"), + CacheKey("Human:1002"), HumanWithIdFragmentImpl.Data( id = "1002", name = "Beast" diff --git a/tests/normalization-tests/src/commonTest/kotlin/com/example/NormalizationTest.kt b/tests/normalization-tests/src/commonTest/kotlin/com/example/NormalizationTest.kt index 50f394e7..518e24f8 100644 --- a/tests/normalization-tests/src/commonTest/kotlin/com/example/NormalizationTest.kt +++ b/tests/normalization-tests/src/commonTest/kotlin/com/example/NormalizationTest.kt @@ -11,8 +11,6 @@ import com.apollographql.cache.normalized.api.CacheKeyGenerator import com.apollographql.cache.normalized.api.CacheKeyGeneratorContext import com.apollographql.cache.normalized.api.CacheKeyResolver import com.apollographql.cache.normalized.api.CacheResolver -import com.apollographql.cache.normalized.api.FieldPolicyCacheResolver -import com.apollographql.cache.normalized.api.KeyFieldsCacheKeyGenerator import com.apollographql.cache.normalized.api.ResolverContext import com.apollographql.cache.normalized.cacheManager import com.apollographql.cache.normalized.fetchPolicy @@ -46,7 +44,8 @@ internal object IdBasedCacheKeyResolver : CacheResolver, CacheKeyGenerator { ?: com.apollographql.cache.normalized.api.TypePolicyCacheKeyGenerator.cacheKeyForObject(obj, context) override fun resolveField(context: ResolverContext): Any? { - return FieldPolicyCacheResolver.resolveField(context) + @Suppress("DEPRECATION") + return com.apollographql.cache.normalized.api.FieldPolicyCacheResolver.resolveField(context) } } @@ -154,7 +153,7 @@ class NormalizationTest { .cacheManager( CacheManager( normalizedCacheFactory = MemoryCacheFactory(), - cacheKeyGenerator = KeyFieldsCacheKeyGenerator(Cache.keyFields), + cacheKeyGenerator = com.apollographql.cache.normalized.api.TypePolicyCacheKeyGenerator(Cache.typePolicies), cacheResolver = object : CacheKeyResolver() { override fun cacheKeyForField(context: ResolverContext): CacheKey? { // Same behavior as FieldPolicyCacheResolver @@ -229,7 +228,7 @@ class NormalizationTest { .cacheManager( CacheManager( normalizedCacheFactory = MemoryCacheFactory(), - cacheKeyGenerator = KeyFieldsCacheKeyGenerator(Cache.keyFields), + cacheKeyGenerator = com.apollographql.cache.normalized.api.TypePolicyCacheKeyGenerator(Cache.typePolicies), cacheResolver = object : CacheResolver { @Suppress("UNCHECKED_CAST") override fun resolveField(context: ResolverContext): Any? { @@ -246,7 +245,8 @@ class NormalizationTest { ) } - return FieldPolicyCacheResolver.resolveField(context) + @Suppress("DEPRECATION") + return com.apollographql.cache.normalized.api.FieldPolicyCacheResolver.resolveField(context) } } ) diff --git a/tests/normalized-cache/build.gradle.kts b/tests/normalized-cache/build.gradle.kts index 088c2460..ea14812a 100644 --- a/tests/normalized-cache/build.gradle.kts +++ b/tests/normalized-cache/build.gradle.kts @@ -49,6 +49,11 @@ apollo { service("main") { packageName.set("main") srcDir(file("src/commonMain/graphql/main")) + + @OptIn(ApolloExperimental::class) + plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin") { + argument("packageName", packageName.get()) + } } service("httpcache") { @@ -65,6 +70,11 @@ apollo { sealedClassesForEnumsMatching.set(listOf("Episode")) generateOptionalOperationVariables.set(false) mapScalar("Color", "kotlin.String") + + @OptIn(ApolloExperimental::class) + plugin("com.apollographql.cache:normalized-cache-apollo-compiler-plugin") { + argument("packageName", packageName.get()) + } } service("circular") { diff --git a/tests/normalized-cache/src/commonTest/kotlin/CacheFlagsTest.kt b/tests/normalized-cache/src/commonTest/kotlin/CacheFlagsTest.kt index c8923d9d..f55ab426 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/CacheFlagsTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/CacheFlagsTest.kt @@ -39,7 +39,7 @@ class CacheFlagsTest { @Test fun doNotStore() = runTest(before = { setUp() }) { val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) apolloClient.enqueueTestResponse(query, data) apolloClient.query(query).doNotStore(true).execute() @@ -73,7 +73,7 @@ class CacheFlagsTest { @Test fun doNotStoreWhenSetInResponse() = runTest { val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) cacheManager = CacheManager(MemoryCacheFactory()) val queueTestNetworkTransport = QueueTestNetworkTransport() diff --git a/tests/normalized-cache/src/commonTest/kotlin/CacheResolverTest.kt b/tests/normalized-cache/src/commonTest/kotlin/CacheResolverTest.kt index 897b2b2f..d753d594 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/CacheResolverTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/CacheResolverTest.kt @@ -18,7 +18,7 @@ class CacheResolverTest { val resolver = object : CacheResolver { override fun resolveField(context: ResolverContext): Any? { return when (context.field.name) { - "hero" -> mapOf("name" to "Luke") + "hero" -> mapOf("name" to "Luke", "__typename" to "Human") else -> DefaultCacheResolver.resolveField(context) } } diff --git a/tests/normalized-cache/src/commonTest/kotlin/ExceptionsTest.kt b/tests/normalized-cache/src/commonTest/kotlin/ExceptionsTest.kt index b84cf316..900108d0 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/ExceptionsTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/ExceptionsTest.kt @@ -97,6 +97,7 @@ class ExceptionsTest { { "data": { "hero": { + "__typename": "Droid", "name": "R2-D2", "friends": null } diff --git a/tests/normalized-cache/src/commonTest/kotlin/FetchPolicyTest.kt b/tests/normalized-cache/src/commonTest/kotlin/FetchPolicyTest.kt index f2b0d8c3..f600e6a8 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/FetchPolicyTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/FetchPolicyTest.kt @@ -74,7 +74,7 @@ class FetchPolicyTest { @Test fun cacheFirst() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) mockServer.enqueueString(query.composeJsonResponse(data)) // First query should hit the network and save in cache @@ -104,7 +104,7 @@ class FetchPolicyTest { fun cacheFirstExecuteThrowing() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient = apolloClient.newBuilder().build() val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) mockServer.enqueueString(query.composeJsonResponse(data)) // First query should hit the network and save in cache @@ -144,7 +144,7 @@ class FetchPolicyTest { apolloClient = apolloClient.newBuilder().build() val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) mockServer.enqueueString(query.composeJsonResponse(data)) // First query should hit the network and save in cache @@ -188,7 +188,7 @@ class FetchPolicyTest { @Test fun networkFirst() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) val call = apolloClient.query(query).fetchPolicy(FetchPolicy.NetworkFirst) @@ -227,7 +227,7 @@ class FetchPolicyTest { fun networkFirstExecuteThrowing() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient = apolloClient.newBuilder().build() val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) val call = apolloClient.query(query).fetchPolicy(FetchPolicy.NetworkFirst) @@ -269,7 +269,7 @@ class FetchPolicyTest { apolloClient = apolloClient.newBuilder().build() val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) val call = apolloClient.query(query).fetchPolicy(FetchPolicy.NetworkFirst) @@ -320,7 +320,7 @@ class FetchPolicyTest { @Test fun cacheOnly() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) // First query should hit the network and save in cache mockServer.enqueueString(query.composeJsonResponse(data)) @@ -340,7 +340,7 @@ class FetchPolicyTest { @Test fun networkOnly() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) val call = apolloClient.query(query).fetchPolicy(FetchPolicy.NetworkOnly) @@ -360,7 +360,7 @@ class FetchPolicyTest { fun networkOnlyThrowing() = runTest(before = { setUp() }, after = { tearDown() }) { apolloClient = apolloClient.newBuilder().build() val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) val call = apolloClient.query(query).fetchPolicy(FetchPolicy.NetworkOnly) @@ -386,7 +386,7 @@ class FetchPolicyTest { @Test fun cacheAndNetwork() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) var caught: Throwable? = null // Initial state: everything fails @@ -458,7 +458,7 @@ class FetchPolicyTest { apolloClient = apolloClient.newBuilder().build() val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) var caught: Throwable? = null // Initial state: everything fails // Cache Error + Network Error => Error @@ -545,7 +545,8 @@ class FetchPolicyTest { this, CharacterNameByIdQuery.Data( CharacterNameByIdQuery.Character( - "Luke" + __typename = "Human", + name = "Luke" ) ) ) @@ -568,7 +569,8 @@ class FetchPolicyTest { this, HeroNameQuery.Data( HeroNameQuery.Hero( - "Leila" + __typename = "Human", + name = "Leila" ) ) ) @@ -597,7 +599,8 @@ class FetchPolicyTest { this, HeroNameQuery.Data( HeroNameQuery.Hero( - "Chewbacca" + __typename = "Wookie", + name = "Chewbacca" ) ) ) @@ -626,7 +629,7 @@ class FetchPolicyTest { @Test fun isFromCache() = runTest(before = { setUp() }, after = { tearDown() }) { val query = HeroNameQuery() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("R2-D2")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero(__typename = "Droid", name = "R2-D2")) mockServer.enqueueString(query.composeJsonResponse(data)) // NetworkOnly / hit diff --git a/tests/normalized-cache/src/commonTest/kotlin/IdCacheKeyGeneratorTest.kt b/tests/normalized-cache/src/commonTest/kotlin/IdCacheKeyGeneratorTest.kt index 682cdf75..69e53597 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/IdCacheKeyGeneratorTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/IdCacheKeyGeneratorTest.kt @@ -28,7 +28,7 @@ class IdCacheKeyGeneratorTest { ) val apolloClient = ApolloClient.Builder().networkTransport(QueueTestNetworkTransport()).cacheManager(cacheManager).build() val query = GetUser2Query("42") - apolloClient.enqueueTestResponse(query, GetUser2Query.Data(GetUser2Query.User2(id = "42", name = "John", email = "a@a.com"))) + apolloClient.enqueueTestResponse(query, GetUser2Query.Data(GetUser2Query.User2(__typename = "User", id = "42", name = "John", email = "a@a.com"))) apolloClient.query(query).fetchPolicy(FetchPolicy.NetworkOnly).execute() val user = apolloClient.query(query).fetchPolicy(FetchPolicy.CacheOnly).execute().dataOrThrow().user2!! assertEquals("John", user.name) @@ -44,7 +44,7 @@ class IdCacheKeyGeneratorTest { ) val apolloClient = ApolloClient.Builder().networkTransport(QueueTestNetworkTransport()).cacheManager(cacheManager).build() val query = GetUserByIdQuery("42") - apolloClient.enqueueTestResponse(query, GetUserByIdQuery.Data(GetUserByIdQuery.UserById(userId = "42", name = "John", email = "a@a.com"))) + apolloClient.enqueueTestResponse(query, GetUserByIdQuery.Data(GetUserByIdQuery.UserById(__typename = "User", userId = "42", name = "John", email = "a@a.com"))) apolloClient.query(query).fetchPolicy(FetchPolicy.NetworkOnly).execute() val user = apolloClient.query(query).fetchPolicy(FetchPolicy.CacheOnly).execute().dataOrThrow().userById!! assertEquals("John", user.name) @@ -64,8 +64,8 @@ class IdCacheKeyGeneratorTest { query1, GetUsersQuery.Data( listOf( - GetUsersQuery.User(id = "42", name = "John", email = "a@a.com"), - GetUsersQuery.User(id = "43", name = "Jane", email = "b@b.com"), + GetUsersQuery.User(__typename = "User", id = "42", name = "John", email = "a@a.com"), + GetUsersQuery.User(__typename = "User", id = "43", name = "Jane", email = "b@b.com"), ) ) ) diff --git a/tests/normalized-cache/src/commonTest/kotlin/NormalizationTest.kt b/tests/normalized-cache/src/commonTest/kotlin/NormalizationTest.kt index 2ca0fdee..7b4938e8 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/NormalizationTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/NormalizationTest.kt @@ -23,7 +23,7 @@ class NormalizationTest { apolloClient.enqueueTestResponse( query, RepositoryListQuery.Data( - listOf(RepositoryListQuery.Repository("42")) + listOf(RepositoryListQuery.Repository(__typename = "Repository", id = "42")) ) ) apolloClient.query(query).execute() diff --git a/tests/normalized-cache/src/commonTest/kotlin/NormalizedCacheThreadingTest.kt b/tests/normalized-cache/src/commonTest/kotlin/NormalizedCacheThreadingTest.kt index 401f4f29..9803098c 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/NormalizedCacheThreadingTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/NormalizedCacheThreadingTest.kt @@ -17,7 +17,6 @@ import kotlin.test.assertNull class NormalizedCacheThreadingTest { @Test fun cacheCreationHappensInBackgroundThread() = runTest { - @Suppress("DEPRECATION") val testThreadName = currentThreadId() // No threading on js if (testThreadName == "js") return@runTest @@ -26,7 +25,6 @@ class NormalizedCacheThreadingTest { .networkTransport(QueueTestNetworkTransport()) .normalizedCache(object : NormalizedCacheFactory() { override fun create(): NormalizedCache { - @Suppress("DEPRECATION") cacheCreateThreadName = currentThreadId() return MemoryCacheFactory().create() } @@ -34,7 +32,7 @@ class NormalizedCacheThreadingTest { assertNull(cacheCreateThreadName) val query = CharacterNameByIdQuery("") - apolloClient.enqueueTestResponse(query, CharacterNameByIdQuery.Data(CharacterNameByIdQuery.Character(""))) + apolloClient.enqueueTestResponse(query, CharacterNameByIdQuery.Data(CharacterNameByIdQuery.Character("", ""))) apolloClient.query(query).execute() println("cacheCreateThreadName: $cacheCreateThreadName") assertNotEquals(testThreadName, cacheCreateThreadName) diff --git a/tests/normalized-cache/src/commonTest/kotlin/NormalizerTest.kt b/tests/normalized-cache/src/commonTest/kotlin/NormalizerTest.kt index 2b411063..c84fbfa5 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/NormalizerTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/NormalizerTest.kt @@ -127,18 +127,18 @@ class NormalizerTest { val records = records(HeroAndFriendsNamesWithIDsQuery(Episode.JEDI), "HeroAndFriendsNameWithIdsResponse.json") val record = records.get(CacheKey.QUERY_ROOT) val heroReference = record!![TEST_FIELD_KEY_JEDI] as CacheKey? - assertEquals(CacheKey("Character:2001"), heroReference) + assertEquals(CacheKey("Droid:2001"), heroReference) val heroRecord = records.get(heroReference!!) assertEquals(heroRecord!!["name"], "R2-D2") assertEquals( listOf( - CacheKey("Character:1000"), - CacheKey("Character:1002"), - CacheKey("Character:1003") + CacheKey("Human:1000"), + CacheKey("Human:1002"), + CacheKey("Human:1003") ), heroRecord["friends"] ) - val luke = records.get(CacheKey("Character:1000")) + val luke = records.get(CacheKey("Human:1000")) assertEquals(luke!!["name"], "Luke Skywalker") } @@ -148,18 +148,18 @@ class NormalizerTest { val records = records(HeroAndFriendsNamesWithIDForParentOnlyQuery(Episode.JEDI), "HeroAndFriendsNameWithIdsParentOnlyResponse.json") val record = records[CacheKey.QUERY_ROOT] val heroReference = record!![TEST_FIELD_KEY_JEDI] as CacheKey? - assertEquals(CacheKey("Character:2001"), heroReference) + assertEquals(CacheKey("Droid:2001"), heroReference) val heroRecord = records.get(heroReference!!) assertEquals(heroRecord!!["name"], "R2-D2") assertEquals( listOf( - CacheKey("Character:2001").append("friends", "0"), - CacheKey("Character:2001").append("friends", "1"), - CacheKey("Character:2001").append("friends", "2") + CacheKey("Droid:2001").append("friends", "0"), + CacheKey("Droid:2001").append("friends", "1"), + CacheKey("Droid:2001").append("friends", "2") ), heroRecord["friends"] ) - val luke = records.get(CacheKey("Character:2001").append("friends", "0")) + val luke = records.get(CacheKey("Droid:2001").append("friends", "0")) assertEquals(luke!!["name"], "Luke Skywalker") } diff --git a/tests/normalized-cache/src/commonTest/kotlin/OptimisticCacheTest.kt b/tests/normalized-cache/src/commonTest/kotlin/OptimisticCacheTest.kt index 41833cd7..a09b4122 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/OptimisticCacheTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/OptimisticCacheTest.kt @@ -59,12 +59,15 @@ class OptimisticCacheTest { val mutationId = uuid4() val data = HeroAndFriendsNamesQuery.Data(HeroAndFriendsNamesQuery.Hero( + "Droid", "R222-D222", listOf( HeroAndFriendsNamesQuery.Friend( + "Human", "SuperMan" ), HeroAndFriendsNamesQuery.Friend( + "Human", "Batman" ) ) @@ -108,12 +111,15 @@ class OptimisticCacheTest { val mutationId = uuid4() val data = HeroAndFriendsNamesFragment( + "Droid", "R222-D222", listOf( HeroAndFriendsNamesFragment.Friend( + "Human", "SuperMan" ), HeroAndFriendsNamesFragment.Friend( + "Human", "Batman" ) ) @@ -159,14 +165,17 @@ class OptimisticCacheTest { // now write some optimistic updates for query1 val data1 = HeroAndFriendsNamesWithIDsQuery.Data( HeroAndFriendsNamesWithIDsQuery.Hero( + "Droid", "2001", "R222-D222", listOf( HeroAndFriendsNamesWithIDsQuery.Friend( + "Human", "1000", "SuperMan" ), HeroAndFriendsNamesWithIDsQuery.Friend( + "Human", "1003", "Batman" ) @@ -200,6 +209,7 @@ class OptimisticCacheTest { // write optimistic data2 val data2 = HeroNameWithIdQuery.Data(HeroNameWithIdQuery.Hero( + "Human", "1000", "Beast" ) @@ -306,9 +316,10 @@ class OptimisticCacheTest { apolloClient.mutation(updateReviewMutation).optimisticUpdates( UpdateReviewMutation.Data( UpdateReviewMutation.UpdateReview( - "empireReview2", - 5, - "Great" + __typename = "Review", + id = "empireReview2", + stars = 5, + commentary = "Great" ) ) ).execute() @@ -329,7 +340,6 @@ class OptimisticCacheTest { assertEquals("Amazing", watcherData?.reviews?.get(2)?.commentary) // after mutation with rolled back optimistic updates - @Suppress("DEPRECATION") watcherData = channel.awaitElement() assertEquals(3, watcherData?.reviews?.size) assertEquals("empireReview1", watcherData?.reviews?.get(0)?.id) @@ -361,14 +371,17 @@ class OptimisticCacheTest { val data1 = HeroAndFriendsNamesWithIDsQuery.Data( HeroAndFriendsNamesWithIDsQuery.Hero( + "Droid", "2001", "R222-D222", listOf( HeroAndFriendsNamesWithIDsQuery.Friend( + "Human", "1000", "Robocop" ), HeroAndFriendsNamesWithIDsQuery.Friend( + "Human", "1003", "Batman" ) @@ -383,6 +396,7 @@ class OptimisticCacheTest { cacheManager.publish(it) } val data2 = HeroNameWithIdQuery.Data(HeroNameWithIdQuery.Hero( + "Human", "1000", "Spiderman" ) diff --git a/tests/normalized-cache/src/commonTest/kotlin/OtherCacheTest.kt b/tests/normalized-cache/src/commonTest/kotlin/OtherCacheTest.kt index 475076f2..bb9ed45d 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/OtherCacheTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/OtherCacheTest.kt @@ -37,7 +37,8 @@ class OtherCacheTest { private lateinit var cacheManager: CacheManager private suspend fun setUp() { - cacheManager = CacheManager(MemoryCacheFactory(), cacheKeyGenerator = IdCacheKeyGenerator(), cacheResolver = IdCacheKeyResolver()) + cacheManager = + CacheManager(MemoryCacheFactory(), cacheKeyGenerator = IdCacheKeyGenerator(keyScope = CacheKey.Scope.SERVICE), cacheResolver = IdCacheKeyResolver(keyScope = CacheKey.Scope.SERVICE)) mockServer = MockServer() apolloClient = ApolloClient.Builder().serverUrl(mockServer.url()).cacheManager(cacheManager).build() } @@ -74,7 +75,7 @@ class OtherCacheTest { // Some details are not present in the master query, we should get a cache miss val e = apolloClient.query(CharacterDetailsQuery("1002")).fetchPolicy(FetchPolicy.CacheOnly).execute().exception as CacheMissException - assertTrue(e.message!!.contains("Object '${CacheKey("Character:1002").keyToString()}' has no field named '__typename'")) + assertTrue(e.message!!.contains("Object '${CacheKey("1002").keyToString()}' has no field named 'birthDate'")) } @@ -178,6 +179,7 @@ class OtherCacheTest { val mutation = UpdateReviewWithoutVariableMutation() val data = UpdateReviewWithoutVariableMutation.Data( UpdateReviewWithoutVariableMutation.UpdateReview( + __typename = "Review", "0", 5, "Great" diff --git a/tests/normalized-cache/src/commonTest/kotlin/StoreTest.kt b/tests/normalized-cache/src/commonTest/kotlin/StoreTest.kt index 4593b476..7f023aee 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/StoreTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/StoreTest.kt @@ -43,7 +43,8 @@ class StoreTest { private lateinit var cacheManager: CacheManager private fun setUp() { - cacheManager = CacheManager(MemoryCacheFactory(), cacheKeyGenerator = IdCacheKeyGenerator(), cacheResolver = IdCacheKeyResolver()) + cacheManager = + CacheManager(MemoryCacheFactory(), cacheKeyGenerator = IdCacheKeyGenerator(keyScope = CacheKey.Scope.SERVICE), cacheResolver = IdCacheKeyResolver(keyScope = CacheKey.Scope.SERVICE)) apolloClient = ApolloClient.Builder().networkTransport(QueueTestNetworkTransport()).cacheManager(cacheManager).build() } @@ -53,7 +54,7 @@ class StoreTest { assertFriendIsCached("1002", "Han Solo") // remove the root query object - var removed = cacheManager.remove(CacheKey("Character:2001")) + var removed = cacheManager.remove(CacheKey("2001")) assertEquals(true, removed) // Trying to get the full response should fail @@ -64,7 +65,7 @@ class StoreTest { assertFriendIsCached("1002", "Han Solo") // remove a single object from the list - removed = cacheManager.remove(CacheKey("Character:1002")) + removed = cacheManager.remove(CacheKey("1002")) assertEquals(true, removed) // Trying to get the full response should fail @@ -86,7 +87,7 @@ class StoreTest { assertFriendIsCached("1003", "Leia Organa") // Now remove multiple keys - val removed = cacheManager.remove(listOf(CacheKey("Character:1002"), CacheKey("Character:1000"))) + val removed = cacheManager.remove(listOf(CacheKey("1002"), CacheKey("1000"))) assertEquals(2, removed) @@ -132,12 +133,14 @@ class StoreTest { HeroAndFriendsWithFragmentsQuery.Hero( __typename = "Droid", heroWithFriendsFragment = HeroWithFriendsFragment( + "Droid", id = "2001", name = "R2-D2", friends = listOf( HeroWithFriendsFragment.Friend( __typename = "Human", humanWithIdFragment = HumanWithIdFragment( + __typename = "Human", id = "1000", name = "Luke Skywalker" ) @@ -151,7 +154,7 @@ class StoreTest { // Remove fields from HeroWithFriendsFragment val fragment = HeroWithFriendsFragmentImpl() - val cacheKey = CacheKey("Character:2001") + val cacheKey = CacheKey("2001") val fragmentData = apolloClient.apolloStore.readFragment( fragment = fragment, cacheKey = cacheKey, @@ -178,7 +181,7 @@ class StoreTest { assertFriendIsCached("1003", "Leia Organa") // test remove root query object - val removed = cacheManager.remove(CacheKey("Character:2001"), true) + val removed = cacheManager.remove(CacheKey("2001"), true) assertEquals(true, removed) // Nothing should be cached anymore @@ -195,7 +198,7 @@ class StoreTest { storeAllFriends() cacheManager.accessCache { - it.remove(CacheKey("Character:1000"), false) + it.remove(CacheKey("1000"), false) } assertFriendIsNotCached("1000") } @@ -215,18 +218,22 @@ class StoreTest { val query = HeroAndFriendsNamesWithIDsQuery(Episode.NEWHOPE) apolloClient.enqueueTestResponse(query, HeroAndFriendsNamesWithIDsQuery.Data( HeroAndFriendsNamesWithIDsQuery.Hero( + "Droid", "2001", "R2-D2", listOf( HeroAndFriendsNamesWithIDsQuery.Friend( + "Human", "1000", "Luke Skywalker" ), HeroAndFriendsNamesWithIDsQuery.Friend( + "Human", "1002", "Han Solo" ), HeroAndFriendsNamesWithIDsQuery.Friend( + "Human", "1003", "Leia Organa" ), diff --git a/tests/normalized-cache/src/commonTest/kotlin/ThreadTests.kt b/tests/normalized-cache/src/commonTest/kotlin/ThreadTests.kt index 684c1c88..c5ee2cbc 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/ThreadTests.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/ThreadTests.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package test import com.apollographql.apollo.ApolloClient @@ -24,7 +22,6 @@ import kotlin.reflect.KClass import kotlin.test.Test class ThreadTests { - @Suppress("DEPRECATION") class MyNormalizedCache(private val mainThreadId: String) : NormalizedCache { val delegate = MemoryCache() override fun merge(record: Record, cacheHeaders: CacheHeaders, recordMerger: RecordMerger): Set { @@ -93,18 +90,16 @@ class ThreadTests { @Test fun cacheIsNotReadFromTheMainThread() = runTest { - @Suppress("DEPRECATION") if (platform() == Platform.Js) { return@runTest } - @Suppress("DEPRECATION") val apolloClient = ApolloClient.Builder() .normalizedCache(MyMemoryCacheFactory(currentThreadId())) .networkTransport(QueueTestNetworkTransport()) .build() - val data = HeroNameQuery.Data(HeroNameQuery.Hero("Luke")) + val data = HeroNameQuery.Data(HeroNameQuery.Hero("Human", "Luke")) val query = HeroNameQuery() apolloClient.enqueueTestResponse(query, data) diff --git a/tests/normalized-cache/src/commonTest/kotlin/WatcherTest.kt b/tests/normalized-cache/src/commonTest/kotlin/WatcherTest.kt index 1b8cef13..ec0ee4e8 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/WatcherTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/WatcherTest.kt @@ -54,26 +54,26 @@ class WatcherTest { apolloClient = ApolloClient.Builder().networkTransport(QueueTestNetworkTransport()).cacheManager(cacheManager).build() } - private val episodeHeroNameData = EpisodeHeroNameQuery.Data(EpisodeHeroNameQuery.Hero("R2-D2")) - private val episodeHeroNameChangedData = EpisodeHeroNameQuery.Data(EpisodeHeroNameQuery.Hero("Artoo")) - private val episodeHeroNameChangedTwoData = EpisodeHeroNameQuery.Data(EpisodeHeroNameQuery.Hero("ArTwo")) + private val episodeHeroNameData = EpisodeHeroNameQuery.Data(EpisodeHeroNameQuery.Hero("Droid", "R2-D2")) + private val episodeHeroNameChangedData = EpisodeHeroNameQuery.Data(EpisodeHeroNameQuery.Hero("Droid", "Artoo")) + private val episodeHeroNameChangedTwoData = EpisodeHeroNameQuery.Data(EpisodeHeroNameQuery.Hero("Droid", "ArTwo")) - private val episodeHeroNameWithIdData = EpisodeHeroNameWithIdQuery.Data(EpisodeHeroNameWithIdQuery.Hero("2001", "R2-D2")) + private val episodeHeroNameWithIdData = EpisodeHeroNameWithIdQuery.Data(EpisodeHeroNameWithIdQuery.Hero("Droid", "2001", "R2-D2")) private val heroAndFriendsNamesWithIDsData = HeroAndFriendsNamesWithIDsQuery.Data( - HeroAndFriendsNamesWithIDsQuery.Hero("2001", "R2-D2", listOf( - HeroAndFriendsNamesWithIDsQuery.Friend("1000", "Luke Skywalker"), - HeroAndFriendsNamesWithIDsQuery.Friend("1002", "Han Solo"), - HeroAndFriendsNamesWithIDsQuery.Friend("1003", "Leia Organa"), + HeroAndFriendsNamesWithIDsQuery.Hero("Droid", "2001", "R2-D2", listOf( + HeroAndFriendsNamesWithIDsQuery.Friend("Human", "1000", "Luke Skywalker"), + HeroAndFriendsNamesWithIDsQuery.Friend("Human", "1002", "Han Solo"), + HeroAndFriendsNamesWithIDsQuery.Friend("Human", "1003", "Leia Organa"), ) ) ) private val heroAndFriendsNamesWithIDsNameChangedData = HeroAndFriendsNamesWithIDsQuery.Data( - HeroAndFriendsNamesWithIDsQuery.Hero("1000", "Luke Skywalker", listOf( - HeroAndFriendsNamesWithIDsQuery.Friend("2001", "Artoo"), - HeroAndFriendsNamesWithIDsQuery.Friend("1002", "Han Solo"), - HeroAndFriendsNamesWithIDsQuery.Friend("1003", "Leia Organa"), + HeroAndFriendsNamesWithIDsQuery.Hero("Human", "1000", "Luke Skywalker", listOf( + HeroAndFriendsNamesWithIDsQuery.Friend("Droid", "2001", "Artoo"), + HeroAndFriendsNamesWithIDsQuery.Friend("Human", "1002", "Han Solo"), + HeroAndFriendsNamesWithIDsQuery.Friend("Human", "1003", "Leia Organa"), ) ) ) @@ -172,6 +172,7 @@ class WatcherTest { // Someone writes to the store directly val data = EpisodeHeroNameWithIdQuery.Data( EpisodeHeroNameWithIdQuery.Hero( + "Droid", "2001", "Artoo" ) diff --git a/tests/normalized-cache/src/commonTest/kotlin/declarativecache/DeclarativeCacheTest.kt b/tests/normalized-cache/src/commonTest/kotlin/declarativecache/DeclarativeCacheTest.kt index 423624aa..2f3d626b 100644 --- a/tests/normalized-cache/src/commonTest/kotlin/declarativecache/DeclarativeCacheTest.kt +++ b/tests/normalized-cache/src/commonTest/kotlin/declarativecache/DeclarativeCacheTest.kt @@ -4,8 +4,6 @@ import com.apollographql.cache.normalized.CacheManager import com.apollographql.cache.normalized.api.CacheHeaders import com.apollographql.cache.normalized.api.CacheKey import com.apollographql.cache.normalized.api.CacheResolver -import com.apollographql.cache.normalized.api.FieldPolicyCacheResolver -import com.apollographql.cache.normalized.api.KeyFieldsCacheKeyGenerator import com.apollographql.cache.normalized.api.ResolverContext import com.apollographql.cache.normalized.memory.MemoryCacheFactory import com.apollographql.cache.normalized.testing.runTest @@ -51,7 +49,8 @@ class DeclarativeCacheTest { @Test fun typePolicyWithAbstractTypes() = runTest { - val cacheManager = CacheManager(MemoryCacheFactory(), cacheKeyGenerator = KeyFieldsCacheKeyGenerator(Cache.keyFields)) + val cacheManager = + CacheManager(MemoryCacheFactory(), cacheKeyGenerator = com.apollographql.cache.normalized.api.TypePolicyCacheKeyGenerator(Cache.typePolicies)) val type2Data = GetType2Query.Data(GetType2Query.Type2(__typename = "Type2", type2Field = "type1Field", interface2KeyField = "42")) cacheManager.writeOperation(GetType2Query(), type2Data) @@ -152,8 +151,8 @@ class DeclarativeCacheTest { val booksCacheResponse = cacheManager.readOperation(booksQuery) val booksData = booksCacheResponse.data!! assertEquals(2, booksData.books.size) - assertEquals(GetBooksQuery.Book("Promo", "42", "Book"), booksData.books[0]) - assertEquals(GetBooksQuery.Book("Other Book", "43", "Book"), booksData.books[1]) + assertEquals(GetBooksQuery.Book(__typename = "Book", title = "Promo", isbn = "42"), booksData.books[0]) + assertEquals(GetBooksQuery.Book(__typename = "Book", title = "Other Book", isbn = "43"), booksData.books[1]) } @Test @@ -169,7 +168,8 @@ class DeclarativeCacheTest { } } - return FieldPolicyCacheResolver.resolveField(context) + @Suppress("DEPRECATION") + return com.apollographql.cache.normalized.api.FieldPolicyCacheResolver.resolveField(context) } } val cacheManager = CacheManager(MemoryCacheFactory(), cacheResolver = cacheResolver) diff --git a/tests/normalized-cache/src/concurrentTest/kotlin/MemoryCacheOnlyTest.kt b/tests/normalized-cache/src/concurrentTest/kotlin/MemoryCacheOnlyTest.kt index b9b700af..2f09be38 100644 --- a/tests/normalized-cache/src/concurrentTest/kotlin/MemoryCacheOnlyTest.kt +++ b/tests/normalized-cache/src/concurrentTest/kotlin/MemoryCacheOnlyTest.kt @@ -26,7 +26,7 @@ class MemoryCacheOnlyTest { val cacheManager = CacheManager(MemoryCacheFactory().chain(SqlNormalizedCacheFactory())).also { it.clearAll() } val apolloClient = ApolloClient.Builder().networkTransport(QueueTestNetworkTransport()).cacheManager(cacheManager).build() val query = GetUserQuery() - apolloClient.enqueueTestResponse(query, GetUserQuery.Data(GetUserQuery.User("John", "a@a.com"))) + apolloClient.enqueueTestResponse(query, GetUserQuery.Data(GetUserQuery.User(__typename = "User", "John", "a@a.com"))) apolloClient.query(query).memoryCacheOnly(true).execute() val dump: Map, Map> = cacheManager.dump() assertEquals(2, dump[MemoryCache::class]!!.size) @@ -37,7 +37,7 @@ class MemoryCacheOnlyTest { fun memoryCacheOnlyDoesNotReadFromSqlCache() = runTest { val cacheManager = CacheManager(MemoryCacheFactory().chain(SqlNormalizedCacheFactory())).also { it.clearAll() } val query = GetUserQuery() - cacheManager.writeOperation(query, GetUserQuery.Data(GetUserQuery.User("John", "a@a.com"))) + cacheManager.writeOperation(query, GetUserQuery.Data(GetUserQuery.User(__typename = "User", "John", "a@a.com"))) val store2 = CacheManager(MemoryCacheFactory().chain(SqlNormalizedCacheFactory())) val apolloClient = ApolloClient.Builder().serverUrl("unused").cacheManager(store2).build() diff --git a/tests/normalized-cache/src/jvmTest/kotlin/CacheConcurrencyTest.kt b/tests/normalized-cache/src/jvmTest/kotlin/CacheConcurrencyTest.kt index f7aafbba..013ec53e 100644 --- a/tests/normalized-cache/src/jvmTest/kotlin/CacheConcurrencyTest.kt +++ b/tests/normalized-cache/src/jvmTest/kotlin/CacheConcurrencyTest.kt @@ -33,7 +33,7 @@ class CacheConcurrencyTest { 0.until(concurrency).map { launch(dispatcher) { val query = CharacterNameByIdQuery((it / 2).toString()) - apolloClient.enqueueTestResponse(query, CharacterNameByIdQuery.Data(CharacterNameByIdQuery.Character(name = it.toString()))) + apolloClient.enqueueTestResponse(query, CharacterNameByIdQuery.Data(CharacterNameByIdQuery.Character("Droid", name = it.toString()))) apolloClient.query(query).execute() } }.joinAll() diff --git a/tests/normalized-cache/src/jvmTest/kotlin/CacheMissLoggingInterceptorTest.kt b/tests/normalized-cache/src/jvmTest/kotlin/CacheMissLoggingInterceptorTest.kt index dc7b2146..62f1c9cf 100644 --- a/tests/normalized-cache/src/jvmTest/kotlin/CacheMissLoggingInterceptorTest.kt +++ b/tests/normalized-cache/src/jvmTest/kotlin/CacheMissLoggingInterceptorTest.kt @@ -41,6 +41,7 @@ class CacheMissLoggingInterceptorTest { { "data": { "hero": { + "__typename": "Human", "name": "Luke" } } diff --git a/tests/pagination/src/commonTest/kotlin/OffsetBasedWithPageAndInputPaginationTest.kt b/tests/pagination/src/commonTest/kotlin/OffsetBasedWithPageAndInputPaginationTest.kt index d9eb47df..35f8a981 100644 --- a/tests/pagination/src/commonTest/kotlin/OffsetBasedWithPageAndInputPaginationTest.kt +++ b/tests/pagination/src/commonTest/kotlin/OffsetBasedWithPageAndInputPaginationTest.kt @@ -6,10 +6,10 @@ import com.apollographql.apollo.api.Executable import com.apollographql.apollo.api.Optional import com.apollographql.apollo.api.json.ApolloJsonElement import com.apollographql.cache.normalized.CacheManager +import com.apollographql.cache.normalized.api.CacheKey import com.apollographql.cache.normalized.api.DefaultFieldKeyGenerator import com.apollographql.cache.normalized.api.FieldKeyContext import com.apollographql.cache.normalized.api.FieldKeyGenerator -import com.apollographql.cache.normalized.api.FieldPolicyCacheResolver import com.apollographql.cache.normalized.api.FieldRecordMerger import com.apollographql.cache.normalized.api.MetadataGenerator import com.apollographql.cache.normalized.api.MetadataGeneratorContext @@ -45,7 +45,7 @@ class OffsetBasedWithPageAndInputPaginationTest { val cacheManager = CacheManager( normalizedCacheFactory = cacheFactory, metadataGenerator = OffsetPaginationMetadataGenerator("UserPage"), - cacheResolver = FieldPolicyCacheResolver, + cacheResolver = com.apollographql.cache.normalized.api.FieldPolicyCacheResolver(keyScope = CacheKey.Scope.TYPE), recordMerger = FieldRecordMerger(OffsetPaginationFieldMerger()), fieldKeyGenerator = UsersFieldKeyGenerator, )