diff --git a/design-docs/Glossary.md b/design-docs/Glossary.md index fcc41654bf2..860d3a2a9b9 100644 --- a/design-docs/Glossary.md +++ b/design-docs/Glossary.md @@ -1,9 +1,6 @@ -# Codegen Glossary - -## Glossary - -A small Glossary of the terms used during codegen. The [GraphQL Spec](https://spec.graphql.org/draft/) does a nice job of defining the common terms like `Field`, `SelectionSet`, etc... so I'm not adding these terms here. But it misses some concepts that we bumped into during codegen and that I'm trying to clarify here. +# Glossary +The [GraphQL Spec](https://spec.graphql.org/draft/) does a nice job of defining common terms like `Field`, `SelectionSet`, etc. but here are a few other concepts that the library deals with, and their definition. ### Response shape @@ -105,3 +102,26 @@ Example: ### Polymorphic field A field that can take several shapes + +### Record + +A shallow map of a response object. Nested objects in the map values are replaced by a cache reference to another Record. + +### Cache key + +A unique identifier for a Record. +By default it is the path formed by all the field keys from the root of the query to the field referencing the Record. +To avoid duplication the Cache key can also be computed from the Record contents, usually using its key fields. + +### Field key + +A key that uniquely identifies a field within a Record. By default composed of the field name and the arguments passed to it. + +### Key fields + +Fields that are used to compute a Cache key for an object. + +### Pagination arguments + +Field arguments that control pagination, e.g. `first`, `after`, etc. They should be omitted when computing a field key so different pages can be merged into the same field. + diff --git a/design-docs/Normalized cache overview.md b/design-docs/Normalized cache overview.md index 7ba4fb34712..6bd1b52e692 100644 --- a/design-docs/Normalized cache overview.md +++ b/design-docs/Normalized cache overview.md @@ -93,7 +93,7 @@ An example: - + @@ -217,7 +217,7 @@ An instance is created when building the `ApolloClient` and held by the `ApolloC ## Record merging -When a `Record` is stored in the cache, it is _merged_ with the existing one at the same key (if any): +When a `Record` is stored in the cache, it is _merged_ with the existing one at the same cache key (if any): - existing fields are replaced with the new value - new fields are added diff --git a/design-docs/Normalized cache pagination.md b/design-docs/Normalized cache pagination.md index 467fd6b928e..3311d071ebd 100644 --- a/design-docs/Normalized cache pagination.md +++ b/design-docs/Normalized cache pagination.md @@ -210,7 +210,7 @@ If your schema uses a different pagination style, you can still use the paginati #### Pagination arguments -The `@fieldPolicy` directive has a `paginationArgs` argument that can be used to specify the arguments that should be omitted from the field name. +The `@fieldPolicy` directive has a `paginationArgs` argument that can be used to specify the arguments that should be omitted from the field key. Going back to the example above with `usersPage`: @@ -221,7 +221,7 @@ extend type Query ``` > [!NOTE] -> This can also be done programmatically by configuring the `ApolloStore` with a `FieldNameGenerator` implementation. +> This can also be done programmatically by configuring the `ApolloStore` with a `FieldKeyGenerator` implementation. With that in place, after fetching the first page, the cache will look like this: @@ -231,7 +231,7 @@ With that in place, after fetching the first page, the cache will look like this | user:1 | id: 1, name: John Smith | | user:2 | id: 2, name: Jane Doe | -The field name no longer includes the `page` argument, which means watching `UsersPage(page = 1)` or any page will observe the same list. +The field key no longer includes the `page` argument, which means watching `UsersPage(page = 1)` or any page will observe the same list. Here's what happens when fetching the second page: @@ -245,7 +245,7 @@ Here's what happens when fetching the second page: The field containing the first page was overwritten by the second page. -This is because the field name is now the same for all pages and the default merging strategy is to overwrite existing fields with the new value. +This is because the field key is now the same for all pages and the default merging strategy is to overwrite existing fields with the new value. #### Record merging diff --git a/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/NormalizedCache.kt b/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/NormalizedCache.kt index 6fc331af13c..d441b57ccfe 100644 --- a/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/NormalizedCache.kt +++ b/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/NormalizedCache.kt @@ -10,7 +10,7 @@ data class NormalizedCache( ) data class Field( - val name: String, + val key: String, val value: FieldValue, ) diff --git a/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/provider/DatabaseNormalizedCacheProvider.kt b/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/provider/DatabaseNormalizedCacheProvider.kt index be2fd46bfd8..c7009b3ba8f 100644 --- a/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/provider/DatabaseNormalizedCacheProvider.kt +++ b/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/provider/DatabaseNormalizedCacheProvider.kt @@ -39,8 +39,8 @@ class DatabaseNormalizedCacheProvider : NormalizedCacheProvider { apolloRecords.map { (key, apolloRecord) -> NormalizedCache.Record( key = key, - fields = apolloRecord.map { (fieldName, fieldValue) -> - Field(fieldName, fieldValue.toFieldValue()) + fields = apolloRecord.map { (fieldKey, fieldValue) -> + Field(fieldKey, fieldValue.toFieldValue()) }, sizeInBytes = apolloRecord.sizeInBytes ) diff --git a/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/ui/FieldTreeTable.kt b/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/ui/FieldTreeTable.kt index ef423816bcf..8e6f06559bc 100644 --- a/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/ui/FieldTreeTable.kt +++ b/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/ui/FieldTreeTable.kt @@ -40,11 +40,14 @@ class FieldTreeTable(selectRecord: (String) -> Unit) : JBTreeTable(FieldTreeTabl is NormalizedCache.FieldValue.StringValue -> append("\"${v.value}\"") is NormalizedCache.FieldValue.NumberValue -> append(v.value.toString()) is NormalizedCache.FieldValue.BooleanValue -> append(v.value.toString()) - is NormalizedCache.FieldValue.ListValue -> append(when (val size = v.value.size) { - 0 -> ApolloBundle.message("normalizedCacheViewer.fields.list.empty") - 1 -> ApolloBundle.message("normalizedCacheViewer.fields.list.single") - else -> ApolloBundle.message("normalizedCacheViewer.fields.list.multiple", size) - }, SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES) + is NormalizedCache.FieldValue.ListValue -> append( + when (val size = v.value.size) { + 0 -> ApolloBundle.message("normalizedCacheViewer.fields.list.empty") + 1 -> ApolloBundle.message("normalizedCacheViewer.fields.list.single") + else -> ApolloBundle.message("normalizedCacheViewer.fields.list.multiple", size) + }, + SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES + ) is NormalizedCache.FieldValue.CompositeValue -> append("{...}", SimpleTextAttributes.GRAY_ITALIC_ATTRIBUTES) NormalizedCache.FieldValue.Null -> append("null") diff --git a/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/ui/FieldTreeTableModel.kt b/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/ui/FieldTreeTableModel.kt index fc2fafbf112..da8c84ac059 100644 --- a/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/ui/FieldTreeTableModel.kt +++ b/intellij-plugin/src/main/kotlin/com/apollographql/ijplugin/normalizedcache/ui/FieldTreeTableModel.kt @@ -14,7 +14,8 @@ class FieldTreeTableModel : ListTreeTableModel( override fun getColumnClass() = TreeTableModel::class.java override fun valueOf(item: Unit) = Unit }, - object : ColumnInfo(ApolloBundle.message("normalizedCacheViewer.fields.column.value")) { + object : + ColumnInfo(ApolloBundle.message("normalizedCacheViewer.fields.column.value")) { override fun getColumnClass() = NormalizedCache.Field::class.java override fun valueOf(item: NormalizedCacheFieldTreeNode) = item.field }, @@ -42,7 +43,7 @@ class FieldTreeTableModel : ListTreeTableModel( class NormalizedCacheFieldTreeNode(val field: NormalizedCache.Field) : DefaultMutableTreeNode() { init { - userObject = field.name + userObject = field.key } } } diff --git a/intellij-plugin/src/main/resources/messages/ApolloBundle.properties b/intellij-plugin/src/main/resources/messages/ApolloBundle.properties index 200f65ad9f9..7164307d057 100644 --- a/intellij-plugin/src/main/resources/messages/ApolloBundle.properties +++ b/intellij-plugin/src/main/resources/messages/ApolloBundle.properties @@ -191,7 +191,7 @@ errorReport.actionText=Open GitHub Issue toolwindow.stripe.NormalizedCacheViewer=Apollo Normalized Cache normalizedCacheViewer.newTab=New Tab normalizedCacheViewer.tabName.empty=Empty -normalizedCacheViewer.fields.column.key=Key +normalizedCacheViewer.fields.column.key=Field Key normalizedCacheViewer.fields.column.value=Value normalizedCacheViewer.toolbar.expandAll=Expand all keys normalizedCacheViewer.toolbar.collapseAll=Collapse all keys @@ -201,7 +201,7 @@ normalizedCacheViewer.toolbar.refresh=Refresh normalizedCacheViewer.empty.message=Open or drag and drop a normalized cache .db file. normalizedCacheViewer.empty.openFile=Open file... normalizedCacheViewer.empty.pullFromDevice=Pull from device -normalizedCacheViewer.records.table.key=Key +normalizedCacheViewer.records.table.key=Cache Key normalizedCacheViewer.records.table.size=Size normalizedCacheViewer.fields.list.empty=[empty] normalizedCacheViewer.fields.list.single=[1 item] diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CompiledGraphQL.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CompiledGraphQL.kt index 32beed48bcb..7ea18f01188 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CompiledGraphQL.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/api/CompiledGraphQL.kt @@ -68,7 +68,7 @@ class CompiledField internal constructor( val value = argument.value.getOrThrow() return if (value is CompiledVariable) { - if (variables.valueMap.containsKey(value.name)) { + if (variables.valueMap.containsKey(value.name)) { Optional.present(variables.valueMap[value.name]) } else { // this argument has a variable value that is absent @@ -100,7 +100,7 @@ class CompiledField internal constructor( /** * Returns a String containing the name of this field as well as encoded arguments. For an example: * `hero({"episode": "Jedi"})` - * This is mostly used internally to compute records. + * This is mostly used internally to compute field keys / cache keys. * * ## Note1: * The argument defaultValues are not added to the name. If the schema changes from: diff --git a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/exception/Exceptions.kt b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/exception/Exceptions.kt index 43c5a907ef1..a733b6b03a5 100644 --- a/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/exception/Exceptions.kt +++ b/libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo3/exception/Exceptions.kt @@ -17,12 +17,12 @@ sealed class ApolloException(message: String? = null, cause: Throwable? = null) /** * A generic exception used when there is no additional context besides the message. */ -class DefaultApolloException(message: String? = null, cause: Throwable? = null): ApolloException(message, cause) +class DefaultApolloException(message: String? = null, cause: Throwable? = null) : ApolloException(message, cause) /** * No data was found */ -class NoDataException(cause: Throwable?): ApolloException("No data was found", cause) +class NoDataException(cause: Throwable?) : ApolloException("No data was found", cause) /** * An I/O error happened: socket closed, DNS issue, TLS problem, file not found, etc... @@ -118,7 +118,7 @@ class JsonDataException(message: String) : ApolloException(message) * * Due to the way the parsers work, it is not possible to distinguish between both cases. */ -class NullOrMissingField(message: String): ApolloException(message) +class NullOrMissingField(message: String) : ApolloException(message) /** * The response could not be parsed because of an I/O exception. @@ -132,8 +132,8 @@ class NullOrMissingField(message: String): ApolloException(message) @Deprecated("ApolloParseException was only used for I/O exceptions and is now mapped to ApolloNetworkException.") class ApolloParseException(message: String? = null, cause: Throwable? = null) : ApolloException(message = message, cause = cause) -class ApolloGraphQLException(val error: Error): ApolloException("GraphQL error: '${error.message}'") { - constructor(errors: List): this(errors.first()) +class ApolloGraphQLException(val error: Error) : ApolloException("GraphQL error: '${error.message}'") { + constructor(errors: List) : this(errors.first()) @Deprecated("Use error instead", level = DeprecationLevel.ERROR) val errors: List = listOf(error) @@ -143,10 +143,17 @@ class ApolloGraphQLException(val error: Error): ApolloException("GraphQL error: * An object/field was missing in the cache * If [fieldName] is null, it means a reference to an object could not be resolved */ - class CacheMissException @ApolloInternal constructor( + /** + * The cache key to the missing object, or to the parent of the missing field if [fieldName] is not null. + */ val key: String, + + /** + * The field key that was missing. If null, it means the object referenced by [key] was missing. + */ val fieldName: String? = null, + stale: Boolean = false, ) : ApolloException(message = message(key, fieldName, stale)) { @@ -156,14 +163,14 @@ class CacheMissException @ApolloInternal constructor( constructor(key: String, fieldName: String?) : this(key, fieldName, false) companion object { - internal fun message(key: String?, fieldName: String?, stale: Boolean): String { - return if (fieldName == null) { - "Object '$key' not found" + internal fun message(cacheKey: String?, fieldKey: String?, stale: Boolean): String { + return if (fieldKey == null) { + "Object '$cacheKey' not found" } else { if (stale) { - "Field '$fieldName' on object '$key' is stale" + "Field '$fieldKey' on object '$cacheKey' is stale" } else { - "Object '$key' has no field named '$fieldName'" + "Object '$cacheKey' has no field named '$fieldKey'" } } } diff --git a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheResolver.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheResolver.kt index 2dbd08651fd..deddf3fa793 100644 --- a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheResolver.kt +++ b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheResolver.kt @@ -65,7 +65,7 @@ interface CacheResolver { * @param variables the variables of the current operation * @param parent the parent object as a map. It can contain the same values as [Record]. Especially, nested objects will be represented * by [CacheKey] - * @param parentId the id of the parent. Mainly used for debugging + * @param parentId the key of the parent. Mainly used for debugging * * @return a value that can go in a [Record]. No type checking is done. It is the responsibility of implementations to return the correct * type @@ -83,10 +83,10 @@ class ResolverContext( val field: CompiledField, val variables: Executable.Variables, val parent: Map, - val parentId: String, + val parentKey: String, val parentType: String, val cacheHeaders: CacheHeaders, - val fieldNameGenerator: FieldNameGenerator, + val fieldKeyGenerator: FieldKeyGenerator, ) /** @@ -112,12 +112,12 @@ object DefaultCacheResolver : CacheResolver { parent: Map, parentId: String, ): Any? { - val name = field.nameWithArguments(variables) - if (!parent.containsKey(name)) { - throw CacheMissException(parentId, name) + val fieldKey = field.nameWithArguments(variables) + if (!parent.containsKey(fieldKey)) { + throw CacheMissException(parentId, fieldKey) } - return parent[name] + return parent[fieldKey] } } @@ -126,12 +126,12 @@ object DefaultCacheResolver : CacheResolver { */ object DefaultApolloResolver : ApolloResolver { override fun resolveField(context: ResolverContext): Any? { - val name = context.fieldNameGenerator.getFieldName(FieldNameContext(context.parentType, context.field, context.variables)) - if (!context.parent.containsKey(name)) { - throw CacheMissException(context.parentId, name) + val fieldKey = context.fieldKeyGenerator.getFieldKey(FieldKeyContext(context.parentType, context.field, context.variables)) + if (!context.parent.containsKey(fieldKey)) { + throw CacheMissException(context.parentKey, fieldKey) } - return context.parent[name] + return context.parent[fieldKey] } } @@ -142,31 +142,29 @@ object DefaultApolloResolver : ApolloResolver { class ReceiveDateApolloResolver(private val maxAge: Int) : ApolloResolver { override fun resolveField(context: ResolverContext): Any? { - val field = context.field val parent = context.parent - val variables = context.variables - val parentId = context.parentId + val parentKey = context.parentKey - val name = field.nameWithArguments(variables) - if (!parent.containsKey(name)) { - throw CacheMissException(parentId, name) + val fieldKey = context.fieldKeyGenerator.getFieldKey(FieldKeyContext(context.parentType, context.field, context.variables)) + if (!parent.containsKey(fieldKey)) { + throw CacheMissException(parentKey, fieldKey) } if (parent is Record) { - val lastUpdated = parent.dates?.get(name) + val lastUpdated = parent.dates?.get(fieldKey) if (lastUpdated != null) { val maxStale = context.cacheHeaders.headerValue(ApolloCacheHeaders.MAX_STALE)?.toLongOrNull() ?: 0L if (maxStale < Long.MAX_VALUE) { val age = currentTimeMillis() / 1000 - lastUpdated if (maxAge + maxStale - age < 0) { - throw CacheMissException(parentId, name, true) + throw CacheMissException(parentKey, fieldKey, true) } } } } - return parent[name] + return parent[fieldKey] } } @@ -184,21 +182,21 @@ class ExpireDateCacheResolver : CacheResolver { parent: Map, parentId: String, ): Any? { - val name = field.nameWithArguments(variables) - if (!parent.containsKey(name)) { - throw CacheMissException(parentId, name) + val fieldKey = field.nameWithArguments(variables) + if (!parent.containsKey(fieldKey)) { + throw CacheMissException(parentId, fieldKey) } if (parent is Record) { - val expires = parent.dates?.get(name) + val expires = parent.dates?.get(fieldKey) if (expires != null) { if (currentTimeMillis() / 1000 - expires >= 0) { - throw CacheMissException(parentId, name, true) + throw CacheMissException(parentId, fieldKey, true) } } } - return parent[name] + return parent[fieldKey] } } diff --git a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/FieldKeyGenerator.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/FieldKeyGenerator.kt new file mode 100644 index 00000000000..be9509ba1f9 --- /dev/null +++ b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/FieldKeyGenerator.kt @@ -0,0 +1,71 @@ +package com.apollographql.apollo3.cache.normalized.api + +import com.apollographql.apollo3.annotations.ApolloExperimental +import com.apollographql.apollo3.api.CompiledField +import com.apollographql.apollo3.api.Executable + +/** + * A generator for field keys. + * + * Field keys uniquely identify fields within their parent [Record]. By default they take the form of the field's name and its encoded + * arguments, for example `hero({"episode": "Jedi"})` (see [CompiledField.nameWithArguments]). + * + * A [FieldKeyGenerator] can be used to customize this format, for instance to exclude certain pagination arguments when storing a + * connection field. + */ +@ApolloExperimental +interface FieldKeyGenerator { + /** + * Returns the field key to use within its parent [Record]. + */ + fun getFieldKey(context: FieldKeyContext): String +} + +/** + * Context passed to the [FieldKeyGenerator.getFieldKey] method. + */ +@ApolloExperimental +class FieldKeyContext( + val parentType: String, + val field: CompiledField, + val variables: Executable.Variables, +) + +/** + * A [FieldKeyGenerator] that returns the field name with its arguments, excluding pagination arguments defined with the + * `@fieldPolicy(forField: "...", paginationArgs: "...")` directive. + * + * @see CompiledField.nameWithArguments + */ +@ApolloExperimental +object DefaultFieldKeyGenerator : FieldKeyGenerator { + override fun getFieldKey(context: FieldKeyContext): String { + return context.field.nameWithArguments(context.variables) + } +} + +/** + * A [FieldKeyGenerator] that generates field keys by excluding + * [Relay connection types](https://relay.dev/graphql/connections.htm#sec-Connection-Types) pagination arguments. + */ +@ApolloExperimental +class ConnectionFieldKeyGenerator(private val connectionFields: Map>) : FieldKeyGenerator { + companion object { + private val paginationArguments = setOf("first", "last", "before", "after") + } + + override fun getFieldKey(context: FieldKeyContext): String { + return if (context.field.name in connectionFields[context.parentType].orEmpty()) { + context.field.newBuilder() + .arguments( + context.field.arguments.filter { argument -> + argument.name !in paginationArguments + } + ) + .build() + .nameWithArguments(context.variables) + } else { + DefaultFieldKeyGenerator.getFieldKey(context) + } + } +} diff --git a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/FieldNameGenerator.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/FieldNameGenerator.kt deleted file mode 100644 index a950e0f7ce8..00000000000 --- a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/FieldNameGenerator.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.apollographql.apollo3.cache.normalized.api - -import com.apollographql.apollo3.annotations.ApolloExperimental -import com.apollographql.apollo3.api.CompiledField -import com.apollographql.apollo3.api.Executable - -/** - * A generator for field names. - * - * For instance, [FieldNameGenerator] can be used to exclude certain pagination arguments when storing a connection field. - */ -@ApolloExperimental -interface FieldNameGenerator { - /** - * Returns the field name to use within its parent [Record]. - */ - fun getFieldName(context: FieldNameContext): String -} - -/** - * Context passed to the [FieldNameGenerator.getFieldName] method. - */ -@ApolloExperimental -class FieldNameContext( - val parentType: String, - val field: CompiledField, - val variables: Executable.Variables, -) - -/** - * A [FieldNameGenerator] that returns the field name with its arguments, excluding pagination arguments defined with the - * `@fieldPolicy(forField: "...", paginationArgs: "...")` directive. - */ -@ApolloExperimental -object DefaultFieldNameGenerator : FieldNameGenerator { - override fun getFieldName(context: FieldNameContext): String { - return context.field.nameWithArguments(context.variables) - } -} - -/** - * A [FieldNameGenerator] that generates field names excluding - * [Relay connection types](https://relay.dev/graphql/connections.htm#sec-Connection-Types) pagination arguments. - */ -@ApolloExperimental -class ConnectionFieldNameGenerator(private val connectionFields: Map>) : FieldNameGenerator { - companion object { - private val paginationArguments = setOf("first", "last", "before", "after") - } - - override fun getFieldName(context: FieldNameContext): String { - return if (context.field.name in connectionFields[context.parentType].orEmpty()) { - context.field.newBuilder() - .arguments( - context.field.arguments.filter { argument -> - argument.name !in paginationArguments - } - ) - .build() - .nameWithArguments(context.variables) - } else { - DefaultFieldNameGenerator.getFieldName(context) - } - } -} diff --git a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/OperationCacheExtensions.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/OperationCacheExtensions.kt index 44b055c7183..84452c8a3af 100644 --- a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/OperationCacheExtensions.kt +++ b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/OperationCacheExtensions.kt @@ -17,16 +17,17 @@ fun Operation.normalize( customScalarAdapters: CustomScalarAdapters, cacheKeyGenerator: CacheKeyGenerator, metadataGenerator: MetadataGenerator = EmptyMetadataGenerator, - fieldNameGenerator: FieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, embeddedFieldsProvider: EmbeddedFieldsProvider = DefaultEmbeddedFieldsProvider, -) = normalize(data, customScalarAdapters, cacheKeyGenerator, metadataGenerator, fieldNameGenerator, embeddedFieldsProvider, CacheKey.rootKey().key) +) = + normalize(data, customScalarAdapters, cacheKeyGenerator, metadataGenerator, fieldKeyGenerator, embeddedFieldsProvider, CacheKey.rootKey().key) fun Executable.normalize( data: D, customScalarAdapters: CustomScalarAdapters, cacheKeyGenerator: CacheKeyGenerator, metadataGenerator: MetadataGenerator = EmptyMetadataGenerator, - fieldNameGenerator: FieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, embeddedFieldsProvider: EmbeddedFieldsProvider = DefaultEmbeddedFieldsProvider, rootKey: String, ): Map { @@ -34,7 +35,7 @@ fun Executable.normalize( adapter().toJson(writer, customScalarAdapters, data) val variables = variables(customScalarAdapters) @Suppress("UNCHECKED_CAST") - return Normalizer(variables, rootKey, cacheKeyGenerator, metadataGenerator, fieldNameGenerator, embeddedFieldsProvider) + return Normalizer(variables, rootKey, cacheKeyGenerator, metadataGenerator, fieldKeyGenerator, embeddedFieldsProvider) .normalize(writer.root() as Map, rootField().selections, rootField().type.rawType()) } @@ -44,7 +45,7 @@ fun Executable.readDataFromCache( cache: ReadOnlyNormalizedCache, cacheResolver: CacheResolver, cacheHeaders: CacheHeaders, - fieldNameGenerator: FieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, ): D { val variables = variables(customScalarAdapters, true) return readInternal( @@ -53,7 +54,7 @@ fun Executable.readDataFromCache( cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ).toData(adapter(), customScalarAdapters, variables) } @@ -64,7 +65,7 @@ fun Executable.readDataFromCache( cache: ReadOnlyNormalizedCache, cacheResolver: CacheResolver, cacheHeaders: CacheHeaders, - fieldNameGenerator: FieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, ): D { val variables = variables(customScalarAdapters, true) return readInternal( @@ -73,7 +74,7 @@ fun Executable.readDataFromCache( cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ).toData(adapter(), customScalarAdapters, variables) } @@ -83,7 +84,7 @@ fun Executable.readDataFromCache( cache: ReadOnlyNormalizedCache, cacheResolver: ApolloResolver, cacheHeaders: CacheHeaders, - fieldNameGenerator: FieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, ): D { val variables = variables(customScalarAdapters, true) return readInternal( @@ -92,7 +93,7 @@ fun Executable.readDataFromCache( cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ).toData(adapter(), customScalarAdapters, variables) } @@ -103,7 +104,7 @@ fun Executable.readDataFromCache( cache: ReadOnlyNormalizedCache, cacheResolver: ApolloResolver, cacheHeaders: CacheHeaders, - fieldNameGenerator: FieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, ): D { val variables = variables(customScalarAdapters, true) return readInternal( @@ -112,7 +113,7 @@ fun Executable.readDataFromCache( cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ).toData(adapter(), customScalarAdapters, variables) } @@ -123,14 +124,14 @@ fun Executable.readDataFromCacheInternal( cacheResolver: CacheResolver, cacheHeaders: CacheHeaders, variables: Executable.Variables, - fieldNameGenerator: FieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator, ): CacheData = readInternal( cacheKey = cacheKey, cache = cache, cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ) @ApolloInternal @@ -140,14 +141,14 @@ fun Executable.readDataFromCacheInternal( cacheResolver: ApolloResolver, cacheHeaders: CacheHeaders, variables: Executable.Variables, - fieldNameGenerator: FieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator, ): CacheData = readInternal( cacheKey = cacheKey, cache = cache, cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ) @@ -157,7 +158,7 @@ private fun Executable.readInternal( cacheResolver: Any, cacheHeaders: CacheHeaders, variables: Executable.Variables, - fieldNameGenerator: FieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator, ): CacheData { return CacheBatchReader( cache = cache, @@ -167,7 +168,7 @@ private fun Executable.readInternal( rootKey = cacheKey.key, rootSelections = rootField().selections, rootTypename = rootField().type.rawType().name, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ).collectData() } @@ -187,5 +188,7 @@ fun CacheData.toData( root = toMap(), ) - return adapter.fromJson(reader, customScalarAdapters.newBuilder().falseVariables(variables.valueMap.filter { it.value == false }.keys).build()) + return adapter.fromJson(reader, customScalarAdapters.newBuilder().falseVariables(variables.valueMap.filter { it.value == false }.keys) + .build() + ) } diff --git a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheBatchReader.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheBatchReader.kt index fe15eb7d927..6b245eb6377 100644 --- a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheBatchReader.kt +++ b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheBatchReader.kt @@ -9,7 +9,7 @@ import com.apollographql.apollo3.cache.normalized.api.CacheData import com.apollographql.apollo3.cache.normalized.api.CacheHeaders import com.apollographql.apollo3.cache.normalized.api.CacheKey import com.apollographql.apollo3.cache.normalized.api.CacheResolver -import com.apollographql.apollo3.cache.normalized.api.FieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.FieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.ReadOnlyNormalizedCache import com.apollographql.apollo3.cache.normalized.api.Record import com.apollographql.apollo3.cache.normalized.api.ResolverContext @@ -30,7 +30,7 @@ internal class CacheBatchReader( private val cacheHeaders: CacheHeaders, private val rootSelections: List, private val rootTypename: String, - private val fieldNameGenerator: FieldNameGenerator, + private val fieldKeyGenerator: FieldKeyGenerator, ) { /** * @param key: the key of the record we need to fetch @@ -64,6 +64,7 @@ internal class CacheBatchReader( is CompiledField -> { state.fields.add(compiledSelection) } + is CompiledFragment -> { if ((typename in compiledSelection.possibleTypes || compiledSelection.typeCondition == parentType) && !compiledSelection.shouldSkip(state.variables.valueMap)) { collect(compiledSelection.selections, parentType, typename, state) @@ -112,7 +113,8 @@ internal class CacheBatchReader( } } - val collectedFields = collectAndMergeSameDirectives(pendingReference.selections, pendingReference.parentType, variables, record["__typename"] as? String) + val collectedFields = + collectAndMergeSameDirectives(pendingReference.selections, pendingReference.parentType, variables, record["__typename"] as? String) val map = collectedFields.mapNotNull { if (it.shouldSkip(variables.valueMap)) { @@ -122,16 +124,19 @@ internal class CacheBatchReader( val value = when (cacheResolver) { is CacheResolver -> cacheResolver.resolveField(it, variables, record, record.key) is ApolloResolver -> { - cacheResolver.resolveField(ResolverContext( - field = it, - variables = variables, - parent = record, - parentId = record.key, - parentType = pendingReference.parentType, - cacheHeaders = cacheHeaders, - fieldNameGenerator = fieldNameGenerator, - )) + cacheResolver.resolveField( + ResolverContext( + field = it, + variables = variables, + parent = record, + parentKey = record.key, + parentType = pendingReference.parentType, + cacheHeaders = cacheHeaders, + fieldKeyGenerator = fieldKeyGenerator, + ) + ) } + else -> throw IllegalStateException() } value.registerCacheKeys(pendingReference.path + it.responseName, it.selections, it.type.rawType().name) @@ -161,11 +166,13 @@ internal class CacheBatchReader( ) ) } + is List<*> -> { forEachIndexed { index, value -> value.registerCacheKeys(path + index, selections, parentType) } } + is Map<*, *> -> { @Suppress("UNCHECKED_CAST") this as Map @@ -178,16 +185,19 @@ internal class CacheBatchReader( val value = when (cacheResolver) { is CacheResolver -> cacheResolver.resolveField(it, variables, this, "") is ApolloResolver -> { - cacheResolver.resolveField(ResolverContext( - field = it, - variables = variables, - parent = this, - parentId = "", - parentType = parentType, - cacheHeaders = cacheHeaders, - fieldNameGenerator = fieldNameGenerator, - )) + cacheResolver.resolveField( + ResolverContext( + field = it, + variables = variables, + parent = this, + parentKey = "", + parentType = parentType, + cacheHeaders = cacheHeaders, + fieldKeyGenerator = fieldKeyGenerator, + ) + ) } + else -> throw IllegalStateException() } value.registerCacheKeys(path + it.responseName, it.selections, it.type.rawType().name) @@ -200,7 +210,7 @@ internal class CacheBatchReader( private data class CacheBatchReaderData( private val data: Map, Map>, - ): CacheData { + ) : CacheData { @Suppress("UNCHECKED_CAST") override fun toMap(): Map { return data[emptyList()].replaceCacheKeys(emptyList()) as Map @@ -211,17 +221,20 @@ internal class CacheBatchReader( is CacheKey -> { data[path].replaceCacheKeys(path) } + is List<*> -> { mapIndexed { index, src -> src.replaceCacheKeys(path + index) } } + is Map<*, *> -> { // This will traverse Map custom scalars but this is ok as it shouldn't contain any CacheKey mapValues { it.value.replaceCacheKeys(path + (it.key as String)) } } + else -> { // Scalar value this diff --git a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/Normalizer.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/Normalizer.kt index 3a77428ea49..fe241ad4311 100644 --- a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/Normalizer.kt +++ b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/Normalizer.kt @@ -14,8 +14,8 @@ import com.apollographql.apollo3.cache.normalized.api.CacheKeyGenerator import com.apollographql.apollo3.cache.normalized.api.CacheKeyGeneratorContext import com.apollographql.apollo3.cache.normalized.api.EmbeddedFieldsContext import com.apollographql.apollo3.cache.normalized.api.EmbeddedFieldsProvider -import com.apollographql.apollo3.cache.normalized.api.FieldNameContext -import com.apollographql.apollo3.cache.normalized.api.FieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.FieldKeyContext +import com.apollographql.apollo3.cache.normalized.api.FieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.MetadataGenerator import com.apollographql.apollo3.cache.normalized.api.MetadataGeneratorContext import com.apollographql.apollo3.cache.normalized.api.Record @@ -29,7 +29,7 @@ internal class Normalizer( private val rootKey: String, private val cacheKeyGenerator: CacheKeyGenerator, private val metadataGenerator: MetadataGenerator, - private val fieldNameGenerator: FieldNameGenerator, + private val fieldKeyGenerator: FieldKeyGenerator, private val embeddedFieldsProvider: EmbeddedFieldsProvider, ) { private val records = mutableMapOf() @@ -83,7 +83,7 @@ internal class Normalizer( .condition(emptyList()) .build() - val fieldKey = fieldNameGenerator.getFieldName(FieldNameContext(parentType.name, mergedField, variables)) + val fieldKey = fieldKeyGenerator.getFieldKey(FieldKeyContext(parentType.name, mergedField, variables)) val base = if (key == CacheKey.rootKey().key) { // If we're at the root level, skip `QUERY_ROOT` altogether to save a few bytes diff --git a/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheResolver.kt b/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheResolver.kt index a0a1f3e52ab..1b2fb0d2de1 100644 --- a/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheResolver.kt +++ b/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheResolver.kt @@ -91,12 +91,12 @@ object DefaultCacheResolver : CacheResolver { parent: Map, parentId: String, ): Any? { - val name = field.nameWithArguments(variables) - if (!parent.containsKey(name)) { - throw CacheMissException(parentId, name) + val fieldKey = field.nameWithArguments(variables) + if (!parent.containsKey(fieldKey)) { + throw CacheMissException(parentId, fieldKey) } - return parent[name] + return parent[fieldKey] } } @@ -115,22 +115,22 @@ class ReceiveDateCacheResolver(private val maxAge: Int) : CacheResolver { parent: Map, parentId: String, ): Any? { - val name = field.nameWithArguments(variables) - if (!parent.containsKey(name)) { - throw CacheMissException(parentId, name) + val fieldKey = field.nameWithArguments(variables) + if (!parent.containsKey(fieldKey)) { + throw CacheMissException(parentId, fieldKey) } if (parent is Record) { - val lastUpdated = parent.date?.get(name) + val lastUpdated = parent.date?.get(fieldKey) if (lastUpdated != null) { - val age = currentTimeMillis()/1000 - lastUpdated + val age = currentTimeMillis() / 1000 - lastUpdated if (age > maxAge) { - throw CacheMissException(parentId, name, true) + throw CacheMissException(parentId, fieldKey, true) } } } - return parent[name] + return parent[fieldKey] } } @@ -148,21 +148,21 @@ class ExpireDateCacheResolver() : CacheResolver { parent: Map, parentId: String, ): Any? { - val name = field.nameWithArguments(variables) - if (!parent.containsKey(name)) { - throw CacheMissException(parentId, name) + val fieldKey = field.nameWithArguments(variables) + if (!parent.containsKey(fieldKey)) { + throw CacheMissException(parentId, fieldKey) } if (parent is Record) { - val expires = parent.date?.get(name) + val expires = parent.date?.get(fieldKey) if (expires != null) { - if (currentTimeMillis()/1000 - expires >= 0) { - throw CacheMissException(parentId, name, true) + if (currentTimeMillis() / 1000 - expires >= 0) { + throw CacheMissException(parentId, fieldKey, true) } } } - return parent[name] + return parent[fieldKey] } } diff --git a/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ApolloStore.kt b/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ApolloStore.kt index c724cbf7bce..40d08691e26 100644 --- a/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ApolloStore.kt +++ b/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ApolloStore.kt @@ -10,11 +10,11 @@ import com.apollographql.apollo3.cache.normalized.api.CacheKey import com.apollographql.apollo3.cache.normalized.api.CacheKeyGenerator import com.apollographql.apollo3.cache.normalized.api.CacheResolver import com.apollographql.apollo3.cache.normalized.api.DefaultEmbeddedFieldsProvider -import com.apollographql.apollo3.cache.normalized.api.DefaultFieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.DefaultFieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.DefaultRecordMerger import com.apollographql.apollo3.cache.normalized.api.EmbeddedFieldsProvider import com.apollographql.apollo3.cache.normalized.api.EmptyMetadataGenerator -import com.apollographql.apollo3.cache.normalized.api.FieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.FieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.FieldPolicyApolloResolver import com.apollographql.apollo3.cache.normalized.api.FieldPolicyCacheResolver import com.apollographql.apollo3.cache.normalized.api.MetadataGenerator @@ -214,7 +214,7 @@ fun ApolloStore( metadataGenerator = EmptyMetadataGenerator, cacheResolver = cacheResolver, recordMerger = DefaultRecordMerger, - fieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator = DefaultFieldKeyGenerator, embeddedFieldsProvider = DefaultEmbeddedFieldsProvider, ) @@ -225,7 +225,7 @@ fun ApolloStore( metadataGenerator: MetadataGenerator = EmptyMetadataGenerator, apolloResolver: ApolloResolver = FieldPolicyApolloResolver, recordMerger: RecordMerger = DefaultRecordMerger, - fieldNameGenerator: FieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, embeddedFieldsProvider: EmbeddedFieldsProvider = DefaultEmbeddedFieldsProvider, ): ApolloStore = DefaultApolloStore( normalizedCacheFactory = normalizedCacheFactory, @@ -233,7 +233,7 @@ fun ApolloStore( metadataGenerator = metadataGenerator, cacheResolver = apolloResolver, recordMerger = recordMerger, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, embeddedFieldsProvider = embeddedFieldsProvider, ) diff --git a/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt b/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt index 2048396f9ad..4132e659139 100644 --- a/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt +++ b/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/ClientCacheExtensions.kt @@ -21,11 +21,11 @@ import com.apollographql.apollo3.cache.normalized.api.CacheHeaders import com.apollographql.apollo3.cache.normalized.api.CacheKeyGenerator import com.apollographql.apollo3.cache.normalized.api.CacheResolver import com.apollographql.apollo3.cache.normalized.api.DefaultEmbeddedFieldsProvider -import com.apollographql.apollo3.cache.normalized.api.DefaultFieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.DefaultFieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.DefaultRecordMerger import com.apollographql.apollo3.cache.normalized.api.EmbeddedFieldsProvider import com.apollographql.apollo3.cache.normalized.api.EmptyMetadataGenerator -import com.apollographql.apollo3.cache.normalized.api.FieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.FieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.FieldPolicyApolloResolver import com.apollographql.apollo3.cache.normalized.api.FieldPolicyCacheResolver import com.apollographql.apollo3.cache.normalized.api.MetadataGenerator @@ -127,7 +127,7 @@ fun ApolloClient.Builder.normalizedCache( metadataGenerator: MetadataGenerator = EmptyMetadataGenerator, apolloResolver: ApolloResolver = FieldPolicyApolloResolver, recordMerger: RecordMerger = DefaultRecordMerger, - fieldNameGenerator: FieldNameGenerator = DefaultFieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator = DefaultFieldKeyGenerator, embeddedFieldsProvider: EmbeddedFieldsProvider = DefaultEmbeddedFieldsProvider, writeToCacheAsynchronously: Boolean = false, ): ApolloClient.Builder { @@ -138,9 +138,10 @@ fun ApolloClient.Builder.normalizedCache( metadataGenerator = metadataGenerator, apolloResolver = apolloResolver, recordMerger = recordMerger, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, embeddedFieldsProvider = embeddedFieldsProvider - ), writeToCacheAsynchronously) + ), writeToCacheAsynchronously + ) } @JvmName("-logCacheMisses") @@ -176,14 +177,16 @@ fun ApolloClient.Builder.store(store: ApolloStore, writeToCacheAsynchronously: B fun ApolloCall.watch( fetchThrows: Boolean, refetchThrows: Boolean, -): Flow> = throw UnsupportedOperationException("watch(fetchThrows: Boolean, refetchThrows: Boolean) is no longer supported, use watch() instead") +): Flow> = + throw UnsupportedOperationException("watch(fetchThrows: Boolean, refetchThrows: Boolean) is no longer supported, use watch() instead") @Deprecated(level = DeprecationLevel.ERROR, message = "Exceptions no longer throw", replaceWith = ReplaceWith("watch()")) @ApolloDeprecatedSince(v4_0_0) @Suppress("UNUSED_PARAMETER") fun ApolloCall.watch( fetchThrows: Boolean, -): Flow> = throw UnsupportedOperationException("watch(fetchThrows: Boolean, refetchThrows: Boolean) is no longer supported, use watch() instead") +): Flow> = + throw UnsupportedOperationException("watch(fetchThrows: Boolean, refetchThrows: Boolean) is no longer supported, use watch() instead") /** * Gets initial response(s) then observes the cache for any changes. @@ -589,7 +592,9 @@ val ApolloResponse.isFromCache: Boolean val ApolloResponse.cacheInfo get() = executionContext[CacheInfo] -internal fun ApolloResponse.withCacheInfo(cacheInfo: CacheInfo) = newBuilder().addExecutionContext(cacheInfo).build() +internal fun ApolloResponse.withCacheInfo(cacheInfo: CacheInfo) = + newBuilder().addExecutionContext(cacheInfo).build() + internal fun ApolloResponse.Builder.cacheInfo(cacheInfo: CacheInfo) = addExecutionContext(cacheInfo) internal class FetchPolicyContext(val interceptor: ApolloInterceptor) : ExecutionContext.Element { @@ -680,7 +685,7 @@ internal val ApolloRequest.fetchFromCache get() = executionContext[FetchFromCacheContext]?.value ?: false fun ApolloResponse.Builder.cacheHeaders(cacheHeaders: CacheHeaders) = - addExecutionContext(CacheHeadersContext(cacheHeaders)) + addExecutionContext(CacheHeadersContext(cacheHeaders)) val ApolloResponse.cacheHeaders get() = executionContext[CacheHeadersContext]?.value ?: CacheHeaders.NONE diff --git a/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/DefaultApolloStore.kt b/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/DefaultApolloStore.kt index cd5c0f52683..82b62968516 100644 --- a/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/DefaultApolloStore.kt +++ b/libraries/apollo-normalized-cache-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/DefaultApolloStore.kt @@ -13,7 +13,7 @@ import com.apollographql.apollo3.cache.normalized.api.CacheKey import com.apollographql.apollo3.cache.normalized.api.CacheKeyGenerator import com.apollographql.apollo3.cache.normalized.api.CacheResolver import com.apollographql.apollo3.cache.normalized.api.EmbeddedFieldsProvider -import com.apollographql.apollo3.cache.normalized.api.FieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.FieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.MetadataGenerator import com.apollographql.apollo3.cache.normalized.api.NormalizedCache import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory @@ -33,7 +33,7 @@ import kotlin.reflect.KClass internal class DefaultApolloStore( normalizedCacheFactory: NormalizedCacheFactory, private val cacheKeyGenerator: CacheKeyGenerator, - private val fieldNameGenerator: FieldNameGenerator, + private val fieldKeyGenerator: FieldKeyGenerator, private val metadataGenerator: MetadataGenerator, private val cacheResolver: Any, private val recordMerger: RecordMerger, @@ -111,7 +111,7 @@ internal class DefaultApolloStore( customScalarAdapters = customScalarAdapters, cacheKeyGenerator = cacheKeyGenerator, metadataGenerator = metadataGenerator, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, embeddedFieldsProvider = embeddedFieldsProvider, ) } @@ -128,7 +128,7 @@ internal class DefaultApolloStore( cacheHeaders = cacheHeaders, cacheKey = CacheKey.rootKey(), variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ).toData(operation.adapter(), customScalarAdapters, variables) } @@ -146,7 +146,7 @@ internal class DefaultApolloStore( cacheHeaders = cacheHeaders, cacheKey = cacheKey, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ).toData(fragment.adapter(), customScalarAdapters, variables) } @@ -165,7 +165,7 @@ internal class DefaultApolloStore( customScalarAdapters = customScalarAdapters, cacheKeyGenerator = cacheKeyGenerator, metadataGenerator = metadataGenerator, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, embeddedFieldsProvider = embeddedFieldsProvider, ).values.toSet() @@ -184,7 +184,7 @@ internal class DefaultApolloStore( customScalarAdapters = customScalarAdapters, cacheKeyGenerator = cacheKeyGenerator, metadataGenerator = metadataGenerator, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, embeddedFieldsProvider = embeddedFieldsProvider, rootKey = cacheKey.key ).values @@ -203,7 +203,7 @@ internal class DefaultApolloStore( customScalarAdapters = customScalarAdapters, cacheKeyGenerator = cacheKeyGenerator, metadataGenerator = metadataGenerator, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, embeddedFieldsProvider = embeddedFieldsProvider, ).values.map { record -> Record( @@ -242,7 +242,7 @@ internal class DefaultApolloStore( cacheResolver: Any, cacheHeaders: CacheHeaders, variables: Executable.Variables, - fieldNameGenerator: FieldNameGenerator, + fieldKeyGenerator: FieldKeyGenerator, ): CacheData { return when (cacheResolver) { is CacheResolver -> readDataFromCacheInternal( @@ -251,7 +251,7 @@ internal class DefaultApolloStore( cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ) is ApolloResolver -> readDataFromCacheInternal( @@ -260,7 +260,7 @@ internal class DefaultApolloStore( cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, variables = variables, - fieldNameGenerator = fieldNameGenerator, + fieldKeyGenerator = fieldKeyGenerator, ) else -> throw IllegalStateException() diff --git a/tests/pagination/src/commonTest/kotlin/ConnectionProgrammaticPaginationTest.kt b/tests/pagination/src/commonTest/kotlin/ConnectionProgrammaticPaginationTest.kt index e5f4d22db18..60bd4adcc5b 100644 --- a/tests/pagination/src/commonTest/kotlin/ConnectionProgrammaticPaginationTest.kt +++ b/tests/pagination/src/commonTest/kotlin/ConnectionProgrammaticPaginationTest.kt @@ -3,7 +3,7 @@ package pagination import com.apollographql.apollo3.api.Optional import com.apollographql.apollo3.cache.normalized.ApolloStore import com.apollographql.apollo3.cache.normalized.api.ConnectionEmbeddedFieldsProvider -import com.apollographql.apollo3.cache.normalized.api.ConnectionFieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.ConnectionFieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.ConnectionMetadataGenerator import com.apollographql.apollo3.cache.normalized.api.ConnectionRecordMerger import com.apollographql.apollo3.cache.normalized.api.FieldPolicyApolloResolver @@ -54,7 +54,7 @@ class ConnectionProgrammaticPaginationTest { metadataGenerator = ConnectionMetadataGenerator(connectionTypes), apolloResolver = FieldPolicyApolloResolver, recordMerger = ConnectionRecordMerger, - fieldNameGenerator = ConnectionFieldNameGenerator(connectionFields), + fieldKeyGenerator = ConnectionFieldKeyGenerator(connectionFields), embeddedFieldsProvider = ConnectionEmbeddedFieldsProvider( connectionTypes = connectionTypes, connectionFields = connectionFields diff --git a/tests/pagination/src/commonTest/kotlin/OffsetBasedWithPageAndInputPaginationTest.kt b/tests/pagination/src/commonTest/kotlin/OffsetBasedWithPageAndInputPaginationTest.kt index 5af35f7023b..d1339ebc929 100644 --- a/tests/pagination/src/commonTest/kotlin/OffsetBasedWithPageAndInputPaginationTest.kt +++ b/tests/pagination/src/commonTest/kotlin/OffsetBasedWithPageAndInputPaginationTest.kt @@ -6,9 +6,9 @@ import com.apollographql.apollo3.api.Executable import com.apollographql.apollo3.api.Optional import com.apollographql.apollo3.api.json.ApolloJsonElement import com.apollographql.apollo3.cache.normalized.ApolloStore -import com.apollographql.apollo3.cache.normalized.api.DefaultFieldNameGenerator -import com.apollographql.apollo3.cache.normalized.api.FieldNameContext -import com.apollographql.apollo3.cache.normalized.api.FieldNameGenerator +import com.apollographql.apollo3.cache.normalized.api.DefaultFieldKeyGenerator +import com.apollographql.apollo3.cache.normalized.api.FieldKeyContext +import com.apollographql.apollo3.cache.normalized.api.FieldKeyGenerator import com.apollographql.apollo3.cache.normalized.api.FieldPolicyApolloResolver import com.apollographql.apollo3.cache.normalized.api.FieldRecordMerger import com.apollographql.apollo3.cache.normalized.api.MemoryCacheFactory @@ -54,7 +54,7 @@ class OffsetBasedWithPageAndInputPaginationTest { metadataGenerator = OffsetPaginationMetadataGenerator("UserPage"), apolloResolver = FieldPolicyApolloResolver, recordMerger = FieldRecordMerger(OffsetPaginationFieldMerger()), - fieldNameGenerator = UsersFieldNameGenerator, + fieldKeyGenerator = UsersFieldKeyGenerator, ) apolloStore.clearAll() @@ -237,12 +237,12 @@ class OffsetBasedWithPageAndInputPaginationTest { } } - object UsersFieldNameGenerator : FieldNameGenerator { - override fun getFieldName(context: FieldNameContext): String { + object UsersFieldKeyGenerator : FieldKeyGenerator { + override fun getFieldKey(context: FieldKeyContext): String { return if (context.parentType == "Query" && context.field.name == "users") { context.field.nameWithoutPaginationArguments(context.variables) } else { - DefaultFieldNameGenerator.getFieldName(context) + DefaultFieldKeyGenerator.getFieldKey(context) } }
KeyCache key Record