From 5244e541668b222fe65660887a7deaeb0590ae23 Mon Sep 17 00:00:00 2001 From: Jan Seeger Date: Fri, 5 Apr 2024 16:03:34 +0200 Subject: [PATCH] Split legacy Firestore Timestamp serializer into legacy (java.util.Date) and new (kotlinx.datetime.Instant) one --- .../common/DefaultSerializersModule.kt | 4 +- ...alizer.kt => TimestampToDateSerializer.kt} | 2 +- .../TimestampToInstantSerializer.kt | 50 +++++++++++++++++++ ...st.kt => TimestampToDateSerializerTest.kt} | 4 +- .../firestore/FirebaseTimestampTests.kt | 12 ++--- ...st.kt => TimestampToDateSerializerTest.kt} | 6 +-- 6 files changed, 64 insertions(+), 14 deletions(-) rename common/src/main/kotlin/de/sipgate/federmappe/common/serializers/{TimestampSerializer.kt => TimestampToDateSerializer.kt} (97%) create mode 100644 common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampToInstantSerializer.kt rename common/src/test/kotlin/de/sipgate/federmappe/common/serializers/{TimestampSerializerTest.kt => TimestampToDateSerializerTest.kt} (94%) rename firestore/src/test/java/de/sipgate/federmappe/firestore/serializers/{TimestampSerializerTest.kt => TimestampToDateSerializerTest.kt} (89%) diff --git a/common/src/main/kotlin/de/sipgate/federmappe/common/DefaultSerializersModule.kt b/common/src/main/kotlin/de/sipgate/federmappe/common/DefaultSerializersModule.kt index 012d404..417bf93 100644 --- a/common/src/main/kotlin/de/sipgate/federmappe/common/DefaultSerializersModule.kt +++ b/common/src/main/kotlin/de/sipgate/federmappe/common/DefaultSerializersModule.kt @@ -1,11 +1,11 @@ package de.sipgate.federmappe.common -import de.sipgate.federmappe.common.serializers.TimestampSerializer +import de.sipgate.federmappe.common.serializers.TimestampToDateSerializer import de.sipgate.federmappe.common.serializers.UriSerializer import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.contextual val DefaultSerializersModule = SerializersModule { - contextual(TimestampSerializer) + contextual(TimestampToDateSerializer) contextual(UriSerializer) } diff --git a/common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampSerializer.kt b/common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampToDateSerializer.kt similarity index 97% rename from common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampSerializer.kt rename to common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampToDateSerializer.kt index 75209f9..af80bbd 100644 --- a/common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampSerializer.kt +++ b/common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampToDateSerializer.kt @@ -12,7 +12,7 @@ import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.encoding.decodeStructure import kotlinx.serialization.encoding.encodeStructure -object TimestampSerializer : KSerializer { +object TimestampToDateSerializer : KSerializer { override val descriptor = buildClassSerialDescriptor(serialName = "FirebaseTimestamp") { element("seconds") element("nanoseconds") diff --git a/common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampToInstantSerializer.kt b/common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampToInstantSerializer.kt new file mode 100644 index 0000000..a5aa577 --- /dev/null +++ b/common/src/main/kotlin/de/sipgate/federmappe/common/serializers/TimestampToInstantSerializer.kt @@ -0,0 +1,50 @@ +package de.sipgate.federmappe.common.serializers + +import kotlin.properties.Delegates +import kotlinx.datetime.Instant +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.buildClassSerialDescriptor +import kotlinx.serialization.descriptors.element +import kotlinx.serialization.encoding.CompositeDecoder +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import kotlinx.serialization.encoding.decodeStructure +import kotlinx.serialization.encoding.encodeStructure + +object TimestampToInstantSerializer : KSerializer { + override val descriptor = buildClassSerialDescriptor(serialName = "FirebaseTimestamp") { + element("seconds") + element("nanoseconds") + } + + override fun serialize(encoder: Encoder, value: Instant) = encoder.encodeStructure(descriptor) { + encodeLongElement(descriptor, 0, value.epochSeconds / 1000) + encodeIntElement(descriptor, 1, value.nanosecondsOfSecond) + } + + @ExperimentalSerializationApi + override fun deserialize(decoder: Decoder): Instant { + return decoder.decodeStructure(descriptor = descriptor) { + if (decodeSequentially()) { + val seconds = decodeLongElement(descriptor, 0) + val nanoSeconds = decodeIntElement(descriptor, 1) + Instant.fromEpochSeconds(seconds, nanoSeconds) + } else { + var seconds by Delegates.notNull() + var nanoSeconds by Delegates.notNull() + + while (true) { + when (val index = decodeElementIndex(descriptor)) { + 0 -> seconds = decodeLongElement(descriptor, 0) + 1 -> nanoSeconds = decodeIntElement(descriptor, 1) + CompositeDecoder.DECODE_DONE -> break + else -> error("Unexpected index: $index") + } + } + + Instant.fromEpochSeconds(seconds, nanoSeconds) + } + } + } +} diff --git a/common/src/test/kotlin/de/sipgate/federmappe/common/serializers/TimestampSerializerTest.kt b/common/src/test/kotlin/de/sipgate/federmappe/common/serializers/TimestampToDateSerializerTest.kt similarity index 94% rename from common/src/test/kotlin/de/sipgate/federmappe/common/serializers/TimestampSerializerTest.kt rename to common/src/test/kotlin/de/sipgate/federmappe/common/serializers/TimestampToDateSerializerTest.kt index e492cbb..d6647c9 100644 --- a/common/src/test/kotlin/de/sipgate/federmappe/common/serializers/TimestampSerializerTest.kt +++ b/common/src/test/kotlin/de/sipgate/federmappe/common/serializers/TimestampToDateSerializerTest.kt @@ -9,7 +9,7 @@ import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test -class TimestampSerializerTest { +class TimestampToDateSerializerTest { private val epochSeconds = 1707833611L * 1000 private val nanos = 801 / 1000000 @@ -20,7 +20,7 @@ class TimestampSerializerTest { @Test fun testDeserialization() { @Serializable - data class TestClass(val a: @Serializable(with = TimestampSerializer::class) Date) + data class TestClass(val a: @Serializable(with = TimestampToDateSerializer::class) Date) val serializer = serializer() val data = mapOf( diff --git a/firestore/src/test/java/de/sipgate/federmappe/firestore/FirebaseTimestampTests.kt b/firestore/src/test/java/de/sipgate/federmappe/firestore/FirebaseTimestampTests.kt index 6e8b747..10b0f9c 100644 --- a/firestore/src/test/java/de/sipgate/federmappe/firestore/FirebaseTimestampTests.kt +++ b/firestore/src/test/java/de/sipgate/federmappe/firestore/FirebaseTimestampTests.kt @@ -2,7 +2,7 @@ package de.sipgate.federmappe.firestore import com.google.firebase.Timestamp import de.sipgate.federmappe.common.StringMapToObjectDecoder -import de.sipgate.federmappe.common.serializers.TimestampSerializer +import de.sipgate.federmappe.common.serializers.TimestampToDateSerializer import java.util.Date import java.util.GregorianCalendar import kotlinx.serialization.Contextual @@ -36,7 +36,7 @@ class FirebaseTimestampTests { StringMapToObjectDecoder( data, ignoreUnknownProperties = true, - serializersModule = SerializersModule { contextual(TimestampSerializer) }, + serializersModule = SerializersModule { contextual(TimestampToDateSerializer) }, subtypeDecoder = { (it as? Timestamp)?.let(::FirebaseTimestampDecoder) } ), ) @@ -51,7 +51,7 @@ class FirebaseTimestampTests { // Arrange @Serializable data class TestClass( - @Serializable(with = TimestampSerializer::class) val a: Date, + @Serializable(with = TimestampToDateSerializer::class) val a: Date, ) val serializer = serializer() @@ -95,7 +95,7 @@ class FirebaseTimestampTests { StringMapToObjectDecoder( data, ignoreUnknownProperties = true, - serializersModule = SerializersModule { contextual(TimestampSerializer) }, + serializersModule = SerializersModule { contextual(TimestampToDateSerializer) }, subtypeDecoder = { (it as? Timestamp)?.let(::FirebaseTimestampDecoder) } ), ) @@ -125,7 +125,7 @@ class FirebaseTimestampTests { StringMapToObjectDecoder( data, ignoreUnknownProperties = true, - serializersModule = SerializersModule { contextual(TimestampSerializer) }, + serializersModule = SerializersModule { contextual(TimestampToDateSerializer) }, ), ) @@ -157,7 +157,7 @@ class FirebaseTimestampTests { StringMapToObjectDecoder( data, ignoreUnknownProperties = true, - serializersModule = SerializersModule { contextual(TimestampSerializer) }, + serializersModule = SerializersModule { contextual(TimestampToDateSerializer) }, subtypeDecoder = { (it as? Timestamp)?.let(::FirebaseTimestampDecoder) } ), ) diff --git a/firestore/src/test/java/de/sipgate/federmappe/firestore/serializers/TimestampSerializerTest.kt b/firestore/src/test/java/de/sipgate/federmappe/firestore/serializers/TimestampToDateSerializerTest.kt similarity index 89% rename from firestore/src/test/java/de/sipgate/federmappe/firestore/serializers/TimestampSerializerTest.kt rename to firestore/src/test/java/de/sipgate/federmappe/firestore/serializers/TimestampToDateSerializerTest.kt index 2465b4d..fd41fd6 100644 --- a/firestore/src/test/java/de/sipgate/federmappe/firestore/serializers/TimestampSerializerTest.kt +++ b/firestore/src/test/java/de/sipgate/federmappe/firestore/serializers/TimestampToDateSerializerTest.kt @@ -3,7 +3,7 @@ package de.sipgate.federmappe.firestore.serializers import com.google.firebase.Timestamp import de.sipgate.federmappe.firestore.FirebaseTimestampDecoder import de.sipgate.federmappe.common.StringMapToObjectDecoder -import de.sipgate.federmappe.common.serializers.TimestampSerializer +import de.sipgate.federmappe.common.serializers.TimestampToDateSerializer import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable import kotlinx.serialization.serializer @@ -12,7 +12,7 @@ import org.junit.jupiter.api.Test import java.util.Date -class TimestampSerializerTest { +class TimestampToDateSerializerTest { private val epochSeconds = 1707833611L * 1000 private val nanos = 801 / 1000000 @@ -23,7 +23,7 @@ class TimestampSerializerTest { @Test fun testDeserialization() { @Serializable - data class TestClass(val a: @Serializable(with = TimestampSerializer::class) Date) + data class TestClass(val a: @Serializable(with = TimestampToDateSerializer::class) Date) val serializer = serializer() val data = mapOf("a" to Timestamp(1707833611, 801))