Skip to content

Commit

Permalink
✨Add support for nested timestamps in normalizer
Browse files Browse the repository at this point in the history
  • Loading branch information
geigi committed Dec 16, 2024
1 parent ac4db31 commit f506e50
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,13 @@ class FirestoreIntegrationTest {
@JvmStatic
fun initializeTestData(): Unit = runTest {
val users = firestore.collection("$tempNamespace-users")
val root = users.add(mapOf<String, Any?>(
"name" to "root",
"createdAt" to Timestamp(1720794610L, 0),
)).await()
val root = users.add(
mapOf<String, Any?>(
"name" to "root",
"createdAt" to Timestamp(1720794610L, 0),
"nested" to mapOf<String, Any?>("updatedAt" to Timestamp(1720794610L, 0))
)
).await()
root.update("id", root.id).await()
}
}
Expand Down Expand Up @@ -100,6 +103,6 @@ class FirestoreIntegrationTest {
}
}
assertTrue(b.isNotEmpty())
assertIs< Entity.FullUser>(b.first())
assertIs<Entity.FullUser>(b.first())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,20 @@ inline fun <reified T : Any> QuerySnapshot.toObjects(
}
}

@Suppress("UNCHECKED_CAST")
fun Map<String, Any>.normalizeStringMap(): Map<String, Any> = mapValues {
when (val value = it.value) {
is Timestamp -> value.toDecodableTimestamp()
is Map<*, *> -> (value as? Map<String, Any>)?.normalizeStringMap() ?: value
else -> value
}
}

@Suppress("UNCHECKED_CAST")
fun Map<String, Any?>.normalizeStringMapNullable(): Map<String, Any?> = mapValues {
when (val value = it.value) {
is Timestamp -> value.toDecodableTimestamp()
is Map<*, *> -> (value as? Map<String, Any>)?.normalizeStringMap() ?: value
else -> value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package de.sipgate.federmappe.firestore.types

import com.google.firebase.Timestamp
import de.sipgate.federmappe.common.decoder.StringMapToObjectDecoder
import de.sipgate.federmappe.firestore.normalizeStringMap
import de.sipgate.federmappe.firestore.normalizeStringMapNullable
import kotlinx.datetime.Instant
import kotlinx.datetime.serializers.InstantComponentSerializer
import kotlinx.datetime.toJavaInstant
Expand Down Expand Up @@ -137,4 +139,82 @@ class FirestoreTimestampToDecodableTimestampTest {
assertEquals(null, result.createdAt)
assertIs<MockLocalDataClass>(result)
}

@OptIn(ExperimentalSerializationApi::class)
@Test
fun nestedFirestoreTimestampIsNormalizedAndDecodedToKotlinInstantCorrectly() {
// Arrange
val expectedInstant = Instant.fromEpochSeconds(1716823455)
val expectedDate = Date.from(expectedInstant.toJavaInstant())
val timestamp = Timestamp(expectedDate)

@Serializable
data class MockNestedDataClass(
@Contextual
val createdAt: Instant
)

@Serializable
data class MockLocalDataClass(
@Contextual
val nested: MockNestedDataClass
)

val serializer = serializer<MockLocalDataClass>()

val data =
mapOf<String, Any>("nested" to mapOf<String, Any>("createdAt" to timestamp)).normalizeStringMap()

// Act
val result =
serializer.deserialize(
StringMapToObjectDecoder(
data = data,
serializersModule = SerializersModule { contextual(InstantComponentSerializer) },
),
)

// Assert
assertEquals(expectedInstant, result.nested.createdAt)
assertIs<MockLocalDataClass>(result)
}

@OptIn(ExperimentalSerializationApi::class)
@Test
fun nestedFirestoreTimestampIsNormalizedAndDecodedToKotlinInstantCorrectlyNullable() {
// Arrange
val expectedInstant = Instant.fromEpochSeconds(1716823455)
val expectedDate = Date.from(expectedInstant.toJavaInstant())
val timestamp = Timestamp(expectedDate)

@Serializable
data class MockNestedDataClass(
@Contextual
val createdAt: Instant
)

@Serializable
data class MockLocalDataClass(
@Contextual
val nested: MockNestedDataClass
)

val serializer = serializer<MockLocalDataClass>()

val data =
mapOf<String, Any?>("nested" to mapOf<String, Any?>("createdAt" to timestamp)).normalizeStringMapNullable()

// Act
val result =
serializer.deserialize(
StringMapToObjectDecoder(
data = data,
serializersModule = SerializersModule { contextual(InstantComponentSerializer) },
),
)

// Assert
assertEquals(expectedInstant, result.nested.createdAt)
assertIs<MockLocalDataClass>(result)
}
}

0 comments on commit f506e50

Please sign in to comment.