Skip to content

Commit

Permalink
🐛 fix firestore timestamp decoding by normalizing the data
Browse files Browse the repository at this point in the history
The firestore module was merged with the common module, we try to find a better way
  • Loading branch information
JBokMan committed Aug 9, 2024
1 parent 33f168f commit 5d8fbc6
Show file tree
Hide file tree
Showing 11 changed files with 114 additions and 152 deletions.
File renamed without changes.
31 changes: 25 additions & 6 deletions common/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
import java.util.Properties

plugins {
alias(libs.plugins.jetbrains.kotlin.jvm)
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
alias(libs.plugins.kotlin.serialization)
`maven-publish`
signing
}

version = versionString

android {
namespace = "de.sipgate.federmappe.firestore"
compileSdk = 34
defaultConfig.minSdk = 23

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}

dependencies {
compileOnly(libs.firebase.firestore)
compileOnly(libs.kotlinx.serialization)

implementation(libs.kotlinx.datetime)

testImplementation(libs.firebase.firestore)
testImplementation(libs.kotlin.test)
testImplementation(libs.kotlinx.serialization)
testImplementation(libs.mockk.agent)
Expand Down Expand Up @@ -62,7 +85,7 @@ publishing {
}

afterEvaluate {
from(components["java"])
from(components["release"])
}
}

Expand All @@ -78,10 +101,6 @@ publishing {
}
}

kotlin {
jvmToolchain(8)
}

signing {
val signingKey: String? by project
val signingPassword: String? by project
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package de.sipgate.federmappe.common.decoder

import de.sipgate.federmappe.common.helper.sortByPrio
import de.sipgate.federmappe.firestore.normalizeStringMap
import de.sipgate.federmappe.firestore.normalizeStringMapNullable
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.PolymorphicKind
Expand Down Expand Up @@ -64,7 +66,8 @@ class StringMapToObjectDecoder(
return this
}

val value = data[key]
val normalizedData = data.normalizeStringMapNullable()
val value = normalizedData[key]
val valueDescriptor = descriptor.kind

when (valueDescriptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,10 @@ fun Map<String, Any>.normalizeStringMap(): Map<String, Any> = mapValues {
else -> value
}
}

fun Map<String, Any?>.normalizeStringMapNullable(): Map<String, Any?> = mapValues {
when (val value = it.value) {
is Timestamp -> value.toDecodableTimestamp()
else -> value
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package de.sipgate.federmappe.common

import com.google.firebase.Timestamp
import de.sipgate.federmappe.common.decoder.StringMapToObjectDecoder
import de.sipgate.federmappe.firestore.types.toDecodableTimestamp
import kotlinx.datetime.Instant
import kotlinx.datetime.serializers.InstantComponentSerializer
import kotlinx.datetime.toJavaInstant
import kotlinx.serialization.Contextual
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.contextual
import kotlinx.serialization.serializer
import java.util.Date
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertTrue

class FirestoreTimestampToDecodableTimestampTest {

@Test
fun timestampWithNanosecondPrecisionIsConvertedSuccessfully() {
val expectedInstant = Instant.fromEpochSeconds(1716823455, 854)

val timestamp = Timestamp(expectedInstant.toJavaInstant())

val result = timestamp.toDecodableTimestamp()
assertEquals(expectedInstant.epochSeconds, result["epochSeconds"])
assertEquals(expectedInstant.nanosecondsOfSecond.toLong(), result["nanosecondsOfSecond"])
}

@Test
fun timestampWithSecondPrecisionIsConvertedSuccessfully() {
val expectedDate = Date.from(Instant.fromEpochSeconds(1716823455).toJavaInstant())
val expectedEpochSeconds = expectedDate.time / 1000

val timestamp = Timestamp(expectedDate)

val result = timestamp.toDecodableTimestamp()

assertEquals(expectedEpochSeconds, result["epochSeconds"])
assertEquals(0L, result["nanosecondsOfSecond"])
}

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

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

val serializer = serializer<MockLocalDataClass>()

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

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

// Assert
assertEquals(expectedInstant, result.createdAt)
assertIs<MockLocalDataClass>(result)
}
}
110 changes: 0 additions & 110 deletions firestore/build.gradle.kts

This file was deleted.

This file was deleted.

0 comments on commit 5d8fbc6

Please sign in to comment.