Skip to content

Allow to store JsonNumber in Record #36

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Record(
* - Map (for custom scalars)
* - null
*/
val fields: Map<String, Any?>,
val fields: Map<String, RecordValue>,
val mutationId: Uuid? = null,
) : Map<String, Any?> by fields {

Expand Down Expand Up @@ -126,3 +126,15 @@ fun Record.receivedDate(field: String) = metadata[field]?.get(ApolloCacheHeaders

@ApolloInternal
fun Record.expirationDate(field: String) = metadata[field]?.get(ApolloCacheHeaders.EXPIRATION_DATE) as? Long


/**
* A typealias for a type-unsafe Kotlin representation of a Record value. This typealias is
* mainly for internal documentation purposes and low-level manipulations and should
* generally be avoided in application code.
*
* [RecordValue] can be any of:
* - [com.apollographql.apollo.api.json.ApolloJsonElement]
* - [CacheKey]
*/
typealias RecordValue = Any?
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.apollographql.cache.normalized.api.internal

import com.apollographql.apollo.annotations.ApolloInternal
import com.apollographql.apollo.api.json.JsonNumber
import com.apollographql.cache.normalized.api.CacheKey
import com.apollographql.cache.normalized.api.Record
import okio.Buffer
Expand Down Expand Up @@ -94,7 +95,12 @@ object BlobRecordSerializer {

is Double -> {
buffer.writeByte(DOUBLE)
buffer.writeString(value.toString())
buffer.writeLong(value.toBits())
}

is JsonNumber -> {
buffer.writeByte(JSON_NUMBER)
buffer.writeString(value.value)
}

is Boolean -> {
Expand Down Expand Up @@ -139,7 +145,8 @@ object BlobRecordSerializer {
STRING -> readString()
INT -> readInt()
LONG -> readLong()
DOUBLE -> readString().toDouble()
DOUBLE -> Double.fromBits(readLong())
JSON_NUMBER -> JsonNumber(readString())
BOOLEAN -> readByte() > 0
CACHE_KEY -> {
CacheKey(readString())
Expand Down Expand Up @@ -169,8 +176,9 @@ object BlobRecordSerializer {
private const val LONG = 2
private const val BOOLEAN = 3
private const val DOUBLE = 4
private const val LIST = 5
private const val MAP = 6
private const val CACHE_KEY = 7
private const val NULL = 8
private const val JSON_NUMBER = 5
private const val LIST = 6
private const val MAP = 7
private const val CACHE_KEY = 8
private const val NULL = 9
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.apollographql.apollo.api.CompiledSelection
import com.apollographql.apollo.api.CompiledType
import com.apollographql.apollo.api.Executable
import com.apollographql.apollo.api.isComposite
import com.apollographql.apollo.api.json.ApolloJsonElement
import com.apollographql.cache.normalized.api.CacheKey
import com.apollographql.cache.normalized.api.CacheKeyGenerator
import com.apollographql.cache.normalized.api.CacheKeyGeneratorContext
Expand All @@ -34,7 +35,11 @@ internal class Normalizer(
) {
private val records = mutableMapOf<String, Record>()

fun normalize(map: Map<String, Any?>, selections: List<CompiledSelection>, parentType: CompiledNamedType): Map<String, Record> {
fun normalize(
map: Map<String, ApolloJsonElement>,
selections: List<CompiledSelection>,
parentType: CompiledNamedType,
): Map<String, Record> {
buildRecord(map, rootKey, selections, parentType)

return records
Expand Down Expand Up @@ -114,7 +119,7 @@ internal class Normalizer(
* @return the CacheKey if this object has a CacheKey or the new Map if the object was embedded
*/
private fun buildRecord(
obj: Map<String, Any?>,
obj: Map<String, ApolloJsonElement>,
key: String,
selections: List<CompiledSelection>,
parentType: CompiledNamedType,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.apollographql.cache.normalized.api.internal

import com.apollographql.apollo.api.json.JsonNumber
import com.apollographql.cache.normalized.api.CacheKey
import com.apollographql.cache.normalized.api.Record
import com.apollographql.cache.normalized.api.RecordValue
import okio.internal.commonAsUtf8ToByteArray
import kotlin.jvm.JvmStatic

internal object RecordWeigher {
Expand Down Expand Up @@ -30,28 +33,30 @@ internal object RecordWeigher {
return size
}

private fun weighField(field: Any?): Int {
private fun weighField(field: RecordValue): Int {
return when (field) {
null -> SIZE_OF_NULL
is String -> field.length
is String -> field.commonAsUtf8ToByteArray().size
is Boolean -> SIZE_OF_BOOLEAN
is Int -> SIZE_OF_INT
is Long -> SIZE_OF_LONG // Might happen with LongDataAdapter
is Double -> SIZE_OF_DOUBLE
is List<*> -> {
SIZE_OF_ARRAY_OVERHEAD + field.sumOf { weighField(it) }
}

is CacheKey -> {
SIZE_OF_CACHE_KEY_OVERHEAD + field.key.length
}
is JsonNumber -> field.value.commonAsUtf8ToByteArray().size + SIZE_OF_LONG
/**
* Custom scalars with a json object representation are stored directly in the record
*/
is Map<*, *> -> {
SIZE_OF_MAP_OVERHEAD + field.keys.sumOf { weighField(it) } + field.values.sumOf { weighField(it) }
}

is List<*> -> {
SIZE_OF_ARRAY_OVERHEAD + field.sumOf { weighField(it) }
}

is CacheKey -> {
SIZE_OF_CACHE_KEY_OVERHEAD + field.key.commonAsUtf8ToByteArray().size
}

else -> error("Unknown field type in Record: '$field'")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.cache.normalized

import com.apollographql.apollo.api.json.JsonNumber
import com.apollographql.cache.normalized.api.CacheKey
import com.apollographql.cache.normalized.api.Record
import kotlin.test.Test
Expand All @@ -13,6 +14,7 @@ class RecordWeigherTest {
val expectedLongValue = Long.MAX_VALUE
val expectedStringValue = "StringValue"
val expectedBooleanValue = true
val expectedNumberValue = JsonNumber("10")
val expectedCacheKey = CacheKey("foo")
val expectedCacheKeyList = listOf(CacheKey("bar"), CacheKey("baz"))
val expectedScalarList = listOf("scalarOne", "scalarTwo")
Expand All @@ -23,13 +25,14 @@ class RecordWeigherTest {
"string" to expectedStringValue,
"boolean" to expectedBooleanValue,
"long" to expectedLongValue,
"number" to expectedNumberValue,
"cacheReference" to expectedCacheKey,
"scalarList" to expectedScalarList,
"referenceList" to expectedCacheKeyList,
)
)

assertTrue(record.sizeInBytes <= 248)
assertTrue(record.sizeInBytes >= 242) // JS takes less space, maybe for strings?
assertTrue(record.sizeInBytes <= 284)
assertTrue(record.sizeInBytes >= 258) // JS takes less space, maybe for strings?
}
}