From 1a3b7f8228111d914565f3b38d3cb55eda615e01 Mon Sep 17 00:00:00 2001 From: "adam.share" Date: Tue, 18 Jul 2023 00:21:34 -0700 Subject: [PATCH 1/3] Move serialization outside of cache lock --- .../api/OperationCacheExtensions.kt | 13 ++-- .../api/internal/CacheBatchReader.kt | 31 +-------- .../api/internal/CacheDataTransformer.kt | 55 +++++++++++++++ .../normalized/internal/DefaultApolloStore.kt | 69 ++++++++++--------- 4 files changed, 97 insertions(+), 71 deletions(-) create mode 100644 libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheDataTransformer.kt 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 40b2249980d..128cc677f01 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 @@ -10,6 +10,7 @@ import com.apollographql.apollo3.api.json.MapJsonWriter import com.apollographql.apollo3.api.toJson import com.apollographql.apollo3.api.variables import com.apollographql.apollo3.cache.normalized.api.internal.CacheBatchReader +import com.apollographql.apollo3.cache.normalized.api.internal.CacheDataTransformer import com.apollographql.apollo3.cache.normalized.api.internal.Normalizer fun Operation.normalize( @@ -57,7 +58,6 @@ fun Executable.normalize( .normalize(writer.root() as Map, rootField().selections, rootField().type.rawType()) } - fun Executable.readDataFromCache( customScalarAdapters: CustomScalarAdapters, cache: ReadOnlyNormalizedCache, @@ -106,8 +106,8 @@ private fun Executable.readInternal( cache: ReadOnlyNormalizedCache, cacheResolver: Any, cacheHeaders: CacheHeaders, -): D { - val map = CacheBatchReader( +): CacheDataTransformer { + return CacheBatchReader( cache = cache, cacheHeaders = cacheHeaders, cacheResolver = cacheResolver, @@ -115,12 +115,7 @@ private fun Executable.readInternal( rootKey = cacheKey.key, rootSelections = rootField().selections, rootTypename = rootField().type.rawType().name - ).toMap() - - val reader = MapJsonReader( - root = map, - ) - return adapter().fromJson(reader, customScalarAdapters) + ).collectData(adapter()) } fun Collection?.dependentKeys(): Set { 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 0908391b163..7144b77b4b5 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 @@ -1,9 +1,9 @@ package com.apollographql.apollo3.cache.normalized.api.internal -import com.apollographql.apollo3.annotations.ApolloInternal import com.apollographql.apollo3.api.CompiledField import com.apollographql.apollo3.api.CompiledFragment import com.apollographql.apollo3.api.CompiledSelection +import com.apollographql.apollo3.api.CompositeAdapter import com.apollographql.apollo3.api.Executable import com.apollographql.apollo3.cache.normalized.api.ApolloResolver import com.apollographql.apollo3.cache.normalized.api.CacheHeaders @@ -83,7 +83,7 @@ internal class CacheBatchReader( } } - fun toMap(): Map { + fun collectData(adapter: CompositeAdapter): CacheDataTransformer { pendingReferences.add( PendingReference( key = rootKey, @@ -132,8 +132,7 @@ internal class CacheBatchReader( } } - @Suppress("UNCHECKED_CAST") - return data[emptyList()].replaceCacheKeys(emptyList()) as Map + return CacheDataTransformer(adapter, data) } /** @@ -179,28 +178,4 @@ internal class CacheBatchReader( } } } - - private fun Any?.replaceCacheKeys(path: List): Any? { - return when (this) { - 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/CacheDataTransformer.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheDataTransformer.kt new file mode 100644 index 00000000000..21a277410a2 --- /dev/null +++ b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheDataTransformer.kt @@ -0,0 +1,55 @@ +package com.apollographql.apollo3.cache.normalized.api.internal + +import com.apollographql.apollo3.annotations.ApolloInternal +import com.apollographql.apollo3.api.CompositeAdapter +import com.apollographql.apollo3.api.CustomScalarAdapters +import com.apollographql.apollo3.api.Executable +import com.apollographql.apollo3.api.fromJson +import com.apollographql.apollo3.api.json.MapJsonReader +import com.apollographql.apollo3.cache.normalized.api.CacheKey + +@ApolloInternal +data class CacheDataTransformer( + private val adapter: CompositeAdapter, + private val data: MutableMap, Map>, +) { + fun toData( + customScalarAdapters: CustomScalarAdapters, + ): D { + val reader = MapJsonReader( + root = toMap(), + ) + return adapter.fromJson(reader, customScalarAdapters) + } + + @Suppress("UNCHECKED_CAST") + private fun toMap(): Map { + return data[emptyList()].replaceCacheKeys(emptyList()) as Map + } + + private fun Any?.replaceCacheKeys(path: List): Any? { + return when (this) { + 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 + } + } + } +} \ No newline at end of file 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 f18568c3c74..b806a59a2d4 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 @@ -16,6 +16,7 @@ import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory import com.apollographql.apollo3.cache.normalized.api.ReadOnlyNormalizedCache import com.apollographql.apollo3.cache.normalized.api.Record import com.apollographql.apollo3.cache.normalized.api.RecordMerger +import com.apollographql.apollo3.cache.normalized.api.internal.CacheDataTransformer import com.apollographql.apollo3.cache.normalized.api.internal.OptimisticCache import com.apollographql.apollo3.cache.normalized.api.normalize import com.apollographql.apollo3.cache.normalized.api.readDataFromCache @@ -115,7 +116,7 @@ internal class DefaultApolloStore( cacheHeaders = cacheHeaders, cacheKey = CacheKey.rootKey() ) - } + }.toData(customScalarAdapters) } override suspend fun readFragment( @@ -132,7 +133,7 @@ internal class DefaultApolloStore( cacheHeaders = cacheHeaders, cacheKey = cacheKey ) - } + }.toData(customScalarAdapters) } @@ -167,15 +168,15 @@ internal class DefaultApolloStore( cacheHeaders: CacheHeaders, publish: Boolean, ): Set { - val changedKeys = lock.write { - val records = fragment.normalize( - data = fragmentData, - customScalarAdapters = customScalarAdapters, - cacheKeyGenerator = cacheKeyGenerator, - metadataGenerator = metadataGenerator, - rootKey = cacheKey.key - ).values + val records = fragment.normalize( + data = fragmentData, + customScalarAdapters = customScalarAdapters, + cacheKeyGenerator = cacheKeyGenerator, + metadataGenerator = metadataGenerator, + rootKey = cacheKey.key + ).values + val changedKeys = lock.write { cache.merge(records, cacheHeaders, recordMerger) } @@ -193,21 +194,22 @@ internal class DefaultApolloStore( publish: Boolean, customScalarAdapters: CustomScalarAdapters, ): Pair, Set> { - val (records, changedKeys) = lock.write { - val records = operation.normalize( - data = operationData, - customScalarAdapters = customScalarAdapters, - cacheKeyGenerator = cacheKeyGenerator, - metadataGenerator = metadataGenerator, - ) + val records = operation.normalize( + data = operationData, + customScalarAdapters = customScalarAdapters, + cacheKeyGenerator = cacheKeyGenerator, + metadataGenerator = metadataGenerator, + ).values.toSet() - records to cache.merge(records.values.toList(), cacheHeaders, recordMerger) + val changedKeys = lock.write { + cache.merge(records, cacheHeaders, recordMerger) } + if (publish) { publish(changedKeys) } - return records.values.toSet() to changedKeys + return records to changedKeys } @@ -218,20 +220,20 @@ internal class DefaultApolloStore( customScalarAdapters: CustomScalarAdapters, publish: Boolean, ): Set { - val changedKeys = lock.write { - val records = operation.normalize( - data = operationData, - customScalarAdapters = customScalarAdapters, - cacheKeyGenerator = cacheKeyGenerator, - metadataGenerator = metadataGenerator, - ).values.map { record -> - Record( - key = record.key, - fields = record.fields, - mutationId = mutationId - ) - } + val records = operation.normalize( + data = operationData, + customScalarAdapters = customScalarAdapters, + cacheKeyGenerator = cacheKeyGenerator, + metadataGenerator = metadataGenerator, + ).values.map { record -> + Record( + key = record.key, + fields = record.fields, + mutationId = mutationId + ) + } + val changedKeys = lock.write { /** * TODO: should we forward the cache headers to the optimistic store? */ @@ -281,7 +283,7 @@ internal class DefaultApolloStore( cache: ReadOnlyNormalizedCache, cacheResolver: Any, cacheHeaders: CacheHeaders, - ): D { + ): CacheDataTransformer { return when (cacheResolver) { is CacheResolver -> readDataFromCache( cacheKey, @@ -304,4 +306,3 @@ internal class DefaultApolloStore( } } } - From e9459e17cb2dc0427733c29cbcb11a2497921d08 Mon Sep 17 00:00:00 2001 From: "adam.share" Date: Wed, 19 Jul 2023 14:00:22 -0700 Subject: [PATCH 2/3] Cache data as interface --- .../apollo3/cache/normalized/api/CacheData.kt | 10 ++++ .../api/OperationCacheExtensions.kt | 16 +++++- .../api/internal/CacheBatchReader.kt | 38 ++++++++++++- .../api/internal/CacheDataTransformer.kt | 55 ------------------- .../normalized/internal/DefaultApolloStore.kt | 9 +-- 5 files changed, 63 insertions(+), 65 deletions(-) create mode 100644 libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt delete mode 100644 libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheDataTransformer.kt diff --git a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt new file mode 100644 index 00000000000..0dad32855e9 --- /dev/null +++ b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt @@ -0,0 +1,10 @@ +package com.apollographql.apollo3.cache.normalized.api + +/** + * Data read from the cache that can be represented as a JSON map. + * + * @see [toData] + */ +interface CacheData { + fun toMap(): Map +} 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 128cc677f01..a37996290ec 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 @@ -1,6 +1,7 @@ package com.apollographql.apollo3.cache.normalized.api import com.apollographql.apollo3.annotations.ApolloExperimental +import com.apollographql.apollo3.api.CompositeAdapter import com.apollographql.apollo3.api.CustomScalarAdapters import com.apollographql.apollo3.api.Executable import com.apollographql.apollo3.api.Operation @@ -10,7 +11,6 @@ import com.apollographql.apollo3.api.json.MapJsonWriter import com.apollographql.apollo3.api.toJson import com.apollographql.apollo3.api.variables import com.apollographql.apollo3.cache.normalized.api.internal.CacheBatchReader -import com.apollographql.apollo3.cache.normalized.api.internal.CacheDataTransformer import com.apollographql.apollo3.cache.normalized.api.internal.Normalizer fun Operation.normalize( @@ -106,7 +106,7 @@ private fun Executable.readInternal( cache: ReadOnlyNormalizedCache, cacheResolver: Any, cacheHeaders: CacheHeaders, -): CacheDataTransformer { +): CacheData { return CacheBatchReader( cache = cache, cacheHeaders = cacheHeaders, @@ -115,7 +115,7 @@ private fun Executable.readInternal( rootKey = cacheKey.key, rootSelections = rootField().selections, rootTypename = rootField().type.rawType().name - ).collectData(adapter()) + ).collectData() } fun Collection?.dependentKeys(): Set { @@ -123,3 +123,13 @@ fun Collection?.dependentKeys(): Set { it.fieldKeys() }?.toSet() ?: emptySet() } + +fun CacheData.toData( + adapter: CompositeAdapter, + customScalarAdapters: CustomScalarAdapters, +): D { + val reader = MapJsonReader( + root = toMap(), + ) + return adapter.fromJson(reader, customScalarAdapters) +} 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 7144b77b4b5..b699d3a7b66 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 @@ -3,9 +3,9 @@ package com.apollographql.apollo3.cache.normalized.api.internal import com.apollographql.apollo3.api.CompiledField import com.apollographql.apollo3.api.CompiledFragment import com.apollographql.apollo3.api.CompiledSelection -import com.apollographql.apollo3.api.CompositeAdapter import com.apollographql.apollo3.api.Executable import com.apollographql.apollo3.cache.normalized.api.ApolloResolver +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 @@ -83,7 +83,7 @@ internal class CacheBatchReader( } } - fun collectData(adapter: CompositeAdapter): CacheDataTransformer { + fun collectData(): CacheData { pendingReferences.add( PendingReference( key = rootKey, @@ -132,7 +132,7 @@ internal class CacheBatchReader( } } - return CacheDataTransformer(adapter, data) + return CacheBatchReaderData(data) } /** @@ -178,4 +178,36 @@ internal class CacheBatchReader( } } } + + private data class CacheBatchReaderData( + private val data: Map, Map>, + ): CacheData { + @Suppress("UNCHECKED_CAST") + override fun toMap(): Map { + return data[emptyList()].replaceCacheKeys(emptyList()) as Map + } + + private fun Any?.replaceCacheKeys(path: List): Any? { + return when (this) { + 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/CacheDataTransformer.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheDataTransformer.kt deleted file mode 100644 index 21a277410a2..00000000000 --- a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheDataTransformer.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.apollographql.apollo3.cache.normalized.api.internal - -import com.apollographql.apollo3.annotations.ApolloInternal -import com.apollographql.apollo3.api.CompositeAdapter -import com.apollographql.apollo3.api.CustomScalarAdapters -import com.apollographql.apollo3.api.Executable -import com.apollographql.apollo3.api.fromJson -import com.apollographql.apollo3.api.json.MapJsonReader -import com.apollographql.apollo3.cache.normalized.api.CacheKey - -@ApolloInternal -data class CacheDataTransformer( - private val adapter: CompositeAdapter, - private val data: MutableMap, Map>, -) { - fun toData( - customScalarAdapters: CustomScalarAdapters, - ): D { - val reader = MapJsonReader( - root = toMap(), - ) - return adapter.fromJson(reader, customScalarAdapters) - } - - @Suppress("UNCHECKED_CAST") - private fun toMap(): Map { - return data[emptyList()].replaceCacheKeys(emptyList()) as Map - } - - private fun Any?.replaceCacheKeys(path: List): Any? { - return when (this) { - 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 - } - } - } -} \ No newline at end of file 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 b806a59a2d4..ec0c7e4f49e 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 @@ -16,10 +16,11 @@ import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory import com.apollographql.apollo3.cache.normalized.api.ReadOnlyNormalizedCache import com.apollographql.apollo3.cache.normalized.api.Record import com.apollographql.apollo3.cache.normalized.api.RecordMerger -import com.apollographql.apollo3.cache.normalized.api.internal.CacheDataTransformer +import com.apollographql.apollo3.cache.normalized.api.CacheData import com.apollographql.apollo3.cache.normalized.api.internal.OptimisticCache import com.apollographql.apollo3.cache.normalized.api.normalize import com.apollographql.apollo3.cache.normalized.api.readDataFromCache +import com.apollographql.apollo3.cache.normalized.api.toData import com.benasher44.uuid.Uuid import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow @@ -116,7 +117,7 @@ internal class DefaultApolloStore( cacheHeaders = cacheHeaders, cacheKey = CacheKey.rootKey() ) - }.toData(customScalarAdapters) + }.toData(operation.adapter(), customScalarAdapters) } override suspend fun readFragment( @@ -133,7 +134,7 @@ internal class DefaultApolloStore( cacheHeaders = cacheHeaders, cacheKey = cacheKey ) - }.toData(customScalarAdapters) + }.toData(fragment.adapter(), customScalarAdapters) } @@ -283,7 +284,7 @@ internal class DefaultApolloStore( cache: ReadOnlyNormalizedCache, cacheResolver: Any, cacheHeaders: CacheHeaders, - ): CacheDataTransformer { + ): CacheData { return when (cacheResolver) { is CacheResolver -> readDataFromCache( cacheKey, From e33a799313ba1e4e9c5aab6a171622df166c7c7e Mon Sep 17 00:00:00 2001 From: Martin Bonnin Date: Thu, 20 Jul 2023 11:52:47 +0200 Subject: [PATCH 3/3] sync non-incubating artifacts and keep backward compat --- .../apollo3/cache/normalized/api/CacheData.kt | 3 + .../api/OperationCacheExtensions.kt | 37 ++++++++- .../apollo3/cache/normalized/api/CacheData.kt | 13 +++ .../api/OperationCacheExtensions.kt | 56 ++++++++++--- .../api/internal/CacheBatchReader.kt | 50 +++++++----- .../normalized/internal/DefaultApolloStore.kt | 14 ++-- .../normalized/internal/DefaultApolloStore.kt | 81 ++++++++++--------- 7 files changed, 174 insertions(+), 80 deletions(-) create mode 100644 libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt diff --git a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt index 0dad32855e9..b3300226bdd 100644 --- a/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt +++ b/libraries/apollo-normalized-cache-api-incubating/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt @@ -1,10 +1,13 @@ package com.apollographql.apollo3.cache.normalized.api +import com.apollographql.apollo3.annotations.ApolloInternal + /** * Data read from the cache that can be represented as a JSON map. * * @see [toData] */ +@ApolloInternal interface CacheData { fun toMap(): Map } 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 a37996290ec..07cd8638d84 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 @@ -1,6 +1,8 @@ package com.apollographql.apollo3.cache.normalized.api +import com.apollographql.apollo3.annotations.ApolloDeprecatedSince import com.apollographql.apollo3.annotations.ApolloExperimental +import com.apollographql.apollo3.annotations.ApolloInternal import com.apollographql.apollo3.api.CompositeAdapter import com.apollographql.apollo3.api.CustomScalarAdapters import com.apollographql.apollo3.api.Executable @@ -63,7 +65,7 @@ fun Executable.readDataFromCache( cache: ReadOnlyNormalizedCache, cacheResolver: CacheResolver, cacheHeaders: CacheHeaders, -) = readInternal( +): D = readDataFromCache( cacheKey = CacheKey.rootKey(), customScalarAdapters = customScalarAdapters, cache = cache, @@ -77,6 +79,35 @@ fun Executable.readDataFromCache( cache: ReadOnlyNormalizedCache, cacheResolver: CacheResolver, cacheHeaders: CacheHeaders, +): D = readInternal( + cacheKey = cacheKey, + customScalarAdapters = customScalarAdapters, + cache = cache, + cacheResolver = cacheResolver, + cacheHeaders = cacheHeaders, +).toData(adapter(), customScalarAdapters) + +fun Executable.readDataFromCache( + cacheKey: CacheKey, + customScalarAdapters: CustomScalarAdapters, + cache: ReadOnlyNormalizedCache, + cacheResolver: ApolloResolver, + cacheHeaders: CacheHeaders, +): D = readInternal( + cacheKey = cacheKey, + customScalarAdapters = customScalarAdapters, + cache = cache, + cacheResolver = cacheResolver, + cacheHeaders = cacheHeaders, +).toData(adapter(), customScalarAdapters) + +@ApolloInternal +fun Executable.readDataFromCacheInternal( + cacheKey: CacheKey, + customScalarAdapters: CustomScalarAdapters, + cache: ReadOnlyNormalizedCache, + cacheResolver: CacheResolver, + cacheHeaders: CacheHeaders, ) = readInternal( cacheKey = cacheKey, customScalarAdapters = customScalarAdapters, @@ -85,7 +116,8 @@ fun Executable.readDataFromCache( cacheHeaders = cacheHeaders, ) -fun Executable.readDataFromCache( +@ApolloInternal +fun Executable.readDataFromCacheInternal( cacheKey: CacheKey, customScalarAdapters: CustomScalarAdapters, cache: ReadOnlyNormalizedCache, @@ -124,6 +156,7 @@ fun Collection?.dependentKeys(): Set { }?.toSet() ?: emptySet() } +@ApolloInternal fun CacheData.toData( adapter: CompositeAdapter, customScalarAdapters: CustomScalarAdapters, diff --git a/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt b/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt new file mode 100644 index 00000000000..b3300226bdd --- /dev/null +++ b/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/CacheData.kt @@ -0,0 +1,13 @@ +package com.apollographql.apollo3.cache.normalized.api + +import com.apollographql.apollo3.annotations.ApolloInternal + +/** + * Data read from the cache that can be represented as a JSON map. + * + * @see [toData] + */ +@ApolloInternal +interface CacheData { + fun toMap(): Map +} diff --git a/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/OperationCacheExtensions.kt b/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/OperationCacheExtensions.kt index 48eaed58a77..6574f24624c 100644 --- a/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/OperationCacheExtensions.kt +++ b/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/OperationCacheExtensions.kt @@ -1,5 +1,7 @@ package com.apollographql.apollo3.cache.normalized.api +import com.apollographql.apollo3.annotations.ApolloInternal +import com.apollographql.apollo3.api.CompositeAdapter import com.apollographql.apollo3.api.CustomScalarAdapters import com.apollographql.apollo3.api.Executable import com.apollographql.apollo3.api.Fragment @@ -12,7 +14,6 @@ import com.apollographql.apollo3.api.variables import com.apollographql.apollo3.cache.normalized.api.internal.CacheBatchReader import com.apollographql.apollo3.cache.normalized.api.internal.Normalizer - fun Operation.normalize( data: D, customScalarAdapters: CustomScalarAdapters, @@ -38,6 +39,34 @@ fun Executable.readDataFromCache( cache: ReadOnlyNormalizedCache, cacheResolver: CacheResolver, cacheHeaders: CacheHeaders, +):D = readInternal( + cacheKey = CacheKey.rootKey(), + customScalarAdapters = customScalarAdapters, + cache = cache, + cacheResolver = cacheResolver, + cacheHeaders = cacheHeaders, +).toData(adapter(), customScalarAdapters) + +fun Fragment.readDataFromCache( + cacheKey: CacheKey, + customScalarAdapters: CustomScalarAdapters, + cache: ReadOnlyNormalizedCache, + cacheResolver: CacheResolver, + cacheHeaders: CacheHeaders, +): D = readInternal( + cacheKey = cacheKey, + customScalarAdapters = customScalarAdapters, + cache = cache, + cacheResolver = cacheResolver, + cacheHeaders = cacheHeaders, +).toData(adapter(), customScalarAdapters) + +@ApolloInternal +fun Executable.readDataFromCacheInternal( + customScalarAdapters: CustomScalarAdapters, + cache: ReadOnlyNormalizedCache, + cacheResolver: CacheResolver, + cacheHeaders: CacheHeaders, ) = readInternal( cacheKey = CacheKey.rootKey(), customScalarAdapters = customScalarAdapters, @@ -46,7 +75,8 @@ fun Executable.readDataFromCache( cacheHeaders = cacheHeaders, ) -fun Fragment.readDataFromCache( +@ApolloInternal +fun Fragment.readDataFromCacheInternal( cacheKey: CacheKey, customScalarAdapters: CustomScalarAdapters, cache: ReadOnlyNormalizedCache, @@ -66,8 +96,8 @@ private fun Executable.readInternal( cache: ReadOnlyNormalizedCache, cacheResolver: CacheResolver, cacheHeaders: CacheHeaders, -): D { - val map = CacheBatchReader( +): CacheData { + return CacheBatchReader( cache = cache, cacheHeaders = cacheHeaders, cacheResolver = cacheResolver, @@ -75,12 +105,7 @@ private fun Executable.readInternal( rootKey = cacheKey.key, rootSelections = rootField().selections, rootTypename = rootField().type.rawType().name - ).toMap() - - val reader = MapJsonReader( - root = map, - ) - return adapter().fromJson(reader, customScalarAdapters) + ).collectData() } fun Collection?.dependentKeys(): Set { @@ -88,3 +113,14 @@ fun Collection?.dependentKeys(): Set { it.fieldKeys() }?.toSet() ?: emptySet() } + +@ApolloInternal +fun CacheData.toData( + adapter: CompositeAdapter, + customScalarAdapters: CustomScalarAdapters, +): D { + val reader = MapJsonReader( + root = toMap(), + ) + return adapter.fromJson(reader, customScalarAdapters) +} diff --git a/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheBatchReader.kt b/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheBatchReader.kt index a39dd528517..38ff6478bb5 100644 --- a/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheBatchReader.kt +++ b/libraries/apollo-normalized-cache-api/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/api/internal/CacheBatchReader.kt @@ -4,6 +4,7 @@ import com.apollographql.apollo3.api.CompiledField import com.apollographql.apollo3.api.CompiledFragment import com.apollographql.apollo3.api.CompiledSelection import com.apollographql.apollo3.api.Executable +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 @@ -79,7 +80,7 @@ internal class CacheBatchReader( } } - fun toMap(): Map { + fun collectData(): CacheData { pendingReferences.add( PendingReference( key = rootKey, @@ -123,8 +124,7 @@ internal class CacheBatchReader( } } - @Suppress("UNCHECKED_CAST") - return data[emptyList()].replaceCacheKeys(emptyList()) as Map + return CacheBatchReaderData(data) } /** @@ -150,27 +150,35 @@ internal class CacheBatchReader( } } - private fun Any?.replaceCacheKeys(path: List): Any? { - return when (this) { - is CacheKey -> { - data[path].replaceCacheKeys(path) - } - is List<*> -> { - mapIndexed { index, src -> - src.replaceCacheKeys(path + index) + private data class CacheBatchReaderData( + private val data: Map, Map>, + ): CacheData { + @Suppress("UNCHECKED_CAST") + override fun toMap(): Map { + return data[emptyList()].replaceCacheKeys(emptyList()) as Map + } + + private fun Any?.replaceCacheKeys(path: List): Any? { + return when (this) { + is CacheKey -> { + data[path].replaceCacheKeys(path) } - } - 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)) + 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 } - } - else -> { - // Scalar value - this } } } } - 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 ec0c7e4f49e..d152f796e0a 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 @@ -6,6 +6,7 @@ import com.apollographql.apollo3.api.Fragment import com.apollographql.apollo3.api.Operation import com.apollographql.apollo3.cache.normalized.ApolloStore import com.apollographql.apollo3.cache.normalized.api.ApolloResolver +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.CacheKeyGenerator @@ -16,10 +17,9 @@ import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory import com.apollographql.apollo3.cache.normalized.api.ReadOnlyNormalizedCache import com.apollographql.apollo3.cache.normalized.api.Record import com.apollographql.apollo3.cache.normalized.api.RecordMerger -import com.apollographql.apollo3.cache.normalized.api.CacheData import com.apollographql.apollo3.cache.normalized.api.internal.OptimisticCache import com.apollographql.apollo3.cache.normalized.api.normalize -import com.apollographql.apollo3.cache.normalized.api.readDataFromCache +import com.apollographql.apollo3.cache.normalized.api.readDataFromCacheInternal import com.apollographql.apollo3.cache.normalized.api.toData import com.benasher44.uuid.Uuid import kotlinx.coroutines.channels.BufferOverflow @@ -110,7 +110,7 @@ internal class DefaultApolloStore( cacheHeaders: CacheHeaders, ): D { return lock.read { - operation.readDataFromCacheInternal( + operation.readDataFromCachePrivate( customScalarAdapters = customScalarAdapters, cache = cache, cacheResolver = cacheResolver, @@ -127,7 +127,7 @@ internal class DefaultApolloStore( cacheHeaders: CacheHeaders, ): D { return lock.read { - fragment.readDataFromCacheInternal( + fragment.readDataFromCachePrivate( customScalarAdapters = customScalarAdapters, cache = cache, cacheResolver = cacheResolver, @@ -278,7 +278,7 @@ internal class DefaultApolloStore( override fun dispose() {} companion object { - private fun Executable.readDataFromCacheInternal( + private fun Executable.readDataFromCachePrivate( cacheKey: CacheKey, customScalarAdapters: CustomScalarAdapters, cache: ReadOnlyNormalizedCache, @@ -286,7 +286,7 @@ internal class DefaultApolloStore( cacheHeaders: CacheHeaders, ): CacheData { return when (cacheResolver) { - is CacheResolver -> readDataFromCache( + is CacheResolver -> readDataFromCacheInternal( cacheKey, customScalarAdapters, cache, @@ -294,7 +294,7 @@ internal class DefaultApolloStore( cacheHeaders ) - is ApolloResolver -> readDataFromCache( + is ApolloResolver -> readDataFromCacheInternal( cacheKey, customScalarAdapters, cache, diff --git a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/DefaultApolloStore.kt b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/DefaultApolloStore.kt index 1a8c2bf84d9..d0d89416bc1 100644 --- a/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/DefaultApolloStore.kt +++ b/libraries/apollo-normalized-cache/src/commonMain/kotlin/com/apollographql/apollo3/cache/normalized/internal/DefaultApolloStore.kt @@ -13,7 +13,8 @@ import com.apollographql.apollo3.cache.normalized.api.NormalizedCacheFactory import com.apollographql.apollo3.cache.normalized.api.Record import com.apollographql.apollo3.cache.normalized.api.internal.OptimisticCache import com.apollographql.apollo3.cache.normalized.api.normalize -import com.apollographql.apollo3.cache.normalized.api.readDataFromCache +import com.apollographql.apollo3.cache.normalized.api.readDataFromCacheInternal +import com.apollographql.apollo3.cache.normalized.api.toData import com.benasher44.uuid.Uuid import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow @@ -100,13 +101,13 @@ internal class DefaultApolloStore( cacheHeaders: CacheHeaders, ): D { return lock.read { - operation.readDataFromCache( + operation.readDataFromCacheInternal( customScalarAdapters = customScalarAdapters, cache = cache, cacheResolver = cacheResolver, cacheHeaders = cacheHeaders, ) - } + }.toData(operation.adapter(), customScalarAdapters) } override suspend fun readFragment( @@ -115,15 +116,15 @@ internal class DefaultApolloStore( customScalarAdapters: CustomScalarAdapters, cacheHeaders: CacheHeaders, ): D { - return lock.read { - fragment.readDataFromCache( - customScalarAdapters = customScalarAdapters, - cache = cache, - cacheResolver = cacheResolver, - cacheHeaders = cacheHeaders, - cacheKey = cacheKey - ) - } + return lock.read { + fragment.readDataFromCacheInternal( + customScalarAdapters = customScalarAdapters, + cache = cache, + cacheResolver = cacheResolver, + cacheHeaders = cacheHeaders, + cacheKey = cacheKey + ) + }.toData(fragment.adapter(), customScalarAdapters) } override suspend fun accessCache(block: (NormalizedCache) -> R): R { @@ -157,15 +158,15 @@ internal class DefaultApolloStore( cacheHeaders: CacheHeaders, publish: Boolean, ): Set { - val changedKeys = lock.write { - val records = fragment.normalize( - data = fragmentData, - customScalarAdapters = customScalarAdapters, - cacheKeyGenerator = cacheKeyGenerator, - rootKey = cacheKey.key - ).values + val records = fragment.normalize( + data = fragmentData, + customScalarAdapters = customScalarAdapters, + cacheKeyGenerator = cacheKeyGenerator, + rootKey = cacheKey.key + ).values - cache.merge(records, cacheHeaders) + val changedKeys = lock.write { + cache.merge(records, cacheHeaders) } if (publish) { @@ -182,20 +183,21 @@ internal class DefaultApolloStore( publish: Boolean, customScalarAdapters: CustomScalarAdapters, ): Pair, Set> { - val (records, changedKeys) = lock.write { - val records = operation.normalize( - data = operationData, - customScalarAdapters = customScalarAdapters, - cacheKeyGenerator = cacheKeyGenerator - ) + val records = operation.normalize( + data = operationData, + customScalarAdapters = customScalarAdapters, + cacheKeyGenerator = cacheKeyGenerator + ).values.toSet() - records to cache.merge(records.values.toList(), cacheHeaders) + val changedKeys = lock.write { + cache.merge(records, cacheHeaders) } + if (publish) { publish(changedKeys) } - return records.values.toSet() to changedKeys + return records to changedKeys } @@ -206,19 +208,18 @@ internal class DefaultApolloStore( customScalarAdapters: CustomScalarAdapters, publish: Boolean, ): Set { + val records = operation.normalize( + data = operationData, + customScalarAdapters = customScalarAdapters, + cacheKeyGenerator = cacheKeyGenerator + ).values.map { record -> + Record( + key = record.key, + fields = record.fields, + mutationId = mutationId + ) + } val changedKeys = lock.write { - val records = operation.normalize( - data = operationData, - customScalarAdapters = customScalarAdapters, - cacheKeyGenerator = cacheKeyGenerator - ).values.map { record -> - Record( - key = record.key, - fields = record.fields, - mutationId = mutationId - ) - } - /** * TODO: should we forward the cache headers to the optimistic store? */