diff --git a/README.md b/README.md index f0546c011..ede0080eb 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Asynchronous operations that return a single or no value are represented by susp suspend fun signInWithCustomToken(token: String): AuthResult ``` -It is important to remember that unlike a callback based API, wating for suspending functions to complete is implicit and so if you don't want to wait for the result you can `launch` a new coroutine: +It is important to remember that unlike a callback based API, waiting for suspending functions to complete is implicit and so if you don't want to wait for the result you can `launch` a new coroutine: ```kotlin //TODO don't use GlobalScope @@ -185,6 +185,44 @@ In combination with a `SerialName` specified for the child class, you have full } ``` +

Serialization of Updates

+Firestore contains update methods that allow for multiple fields to be updated at the same time. +This sdk offers special update methods that allow for applying custom serialization to each individual field though an update builder. +Where an `update` method exists, an `updateFields` method will also be available. In this, each value can have its serializer customized: + +```kotlin +documentRef.updateFields { + "field" to "value" + // Set the value of otherField to "1" using a custom Serializer + "otherField".to(IntAsStringSerializer(), 1) + + // Overwrite build settings. All fields added within this block will have these build settings applied + withEncodeSettings { + encodeDefaults = true + serializersModule = module + "city" to abstractCity + } +} +``` + +Similarly, the `Query` methods `startAt`/`startAfter`/`endAt`/`endBefore` have an alternative method in `startAtFieldValues`/`startAfterFieldValues`/`endAtFieldValues`/`endBeforeFieldValues` + +```kotlin +query.orderBy("field", "otherField", "city").startAtFieldValues { // similar syntax for startAfter/endAt/endBefore + add("Value") + + // Starts at "1" for the otherField value + add(1, IntAsStringSerializer()) + + // Overwrite build settings. All field values added within this block will have these build settings applied + withEncodeSettings { + encodeDefaults = true + serializersModule = module + add(abstractCity) + } +} +``` +

Default arguments

To reduce boilerplate, default arguments are used in the places where the Firebase Android SDK employs the builder pattern: @@ -209,8 +247,6 @@ user.updateProfile(profileUpdates) user.updateProfile(displayName = "Jane Q. User", photoURL = "https://example.com/jane-q-user/profile.jpg") ``` - -

Infix notation

To improve readability and reduce boilerplate for functions such as the Cloud Firestore query operators are built with infix notation: diff --git a/firebase-common-internal/api/android/firebase-common-internal.api b/firebase-common-internal/api/android/firebase-common-internal.api index 44393d3b9..49e83fd95 100644 --- a/firebase-common-internal/api/android/firebase-common-internal.api +++ b/firebase-common-internal/api/android/firebase-common-internal.api @@ -78,59 +78,9 @@ public final class dev/gitlive/firebase/internal/EncodersKt { public static final fun encode (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Z)Ljava/lang/Object; public static final fun encodeAsObject (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/internal/EncodedObject; public static synthetic fun encodeAsObject$default (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/internal/EncodedObject; - public static final fun withSerializer (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)Ljava/lang/Object; -} - -public final class dev/gitlive/firebase/internal/FirebaseClassDecoder : dev/gitlive/firebase/internal/FirebaseCompositeDecoder { - public fun (ILdev/gitlive/firebase/DecodeSettings;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V - public fun decodeElementIndex (Lkotlinx/serialization/descriptors/SerialDescriptor;)I - public fun decodeSequentially ()Z -} - -public class dev/gitlive/firebase/internal/FirebaseCompositeDecoder : kotlinx/serialization/encoding/CompositeDecoder { - public fun (ILdev/gitlive/firebase/DecodeSettings;Lkotlin/jvm/functions/Function2;)V - public fun decodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z - public fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B - public fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C - public fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I - public fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D - public fun decodeElementIndex (Lkotlinx/serialization/descriptors/SerialDescriptor;)I - public fun decodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)F - public fun decodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Decoder; - public fun decodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)I - public fun decodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)J - public fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; - public fun decodeSequentially ()Z - public fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; - public fun decodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)S - public fun decodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String; - public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V - public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; -} - -public class dev/gitlive/firebase/internal/FirebaseCompositeEncoder : kotlinx/serialization/encoding/CompositeEncoder { - public fun (Ldev/gitlive/firebase/EncodeSettings;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;)V - public synthetic fun (Ldev/gitlive/firebase/EncodeSettings;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V - public fun encodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IB)V - public fun encodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IC)V - public fun encodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ID)V - public fun encodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IF)V - public fun encodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Encoder; - public fun encodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;II)V - public fun encodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IJ)V - public fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V - public final fun encodeObject (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/Object;)V - public final fun encodePolymorphicClassDiscriminator (Ljava/lang/String;Ljava/lang/String;)V - public fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V - public fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V - public fun encodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V - public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V - public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; - public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z } -public final class dev/gitlive/firebase/internal/FirebaseDecoder : kotlinx/serialization/encoding/Decoder { +public final class dev/gitlive/firebase/internal/FirebaseDecoderImpl : dev/gitlive/firebase/FirebaseDecoder { public fun (Ljava/lang/Object;)V public fun (Ljava/lang/Object;Ldev/gitlive/firebase/DecodeSettings;)V public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder; @@ -153,7 +103,7 @@ public final class dev/gitlive/firebase/internal/FirebaseDecoder : kotlinx/seria public final fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/internal/FirebaseEncoder : kotlinx/serialization/encoding/Encoder { +public final class dev/gitlive/firebase/internal/FirebaseEncoderImpl : dev/gitlive/firebase/FirebaseEncoder { public fun (Ldev/gitlive/firebase/EncodeSettings;)V public fun (Z)V public fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; @@ -217,25 +167,3 @@ public final class dev/gitlive/firebase/internal/SpecialValueSerializer : kotlin public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } -public final class dev/gitlive/firebase/internal/ValueWithSerializer { - public fun (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)V - public final fun component1 ()Ljava/lang/Object; - public final fun component2 ()Lkotlinx/serialization/SerializationStrategy; - public final fun copy (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)Ldev/gitlive/firebase/internal/ValueWithSerializer; - public static synthetic fun copy$default (Ldev/gitlive/firebase/internal/ValueWithSerializer;Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;ILjava/lang/Object;)Ldev/gitlive/firebase/internal/ValueWithSerializer; - public fun equals (Ljava/lang/Object;)Z - public final fun getSerializer ()Lkotlinx/serialization/SerializationStrategy; - public final fun getValue ()Ljava/lang/Object; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class dev/gitlive/firebase/internal/_decodersKt { - public static final fun getPolymorphicType (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String; - public static final fun structureDecoder (Ldev/gitlive/firebase/internal/FirebaseDecoder;Lkotlinx/serialization/descriptors/SerialDescriptor;Z)Lkotlinx/serialization/encoding/CompositeDecoder; -} - -public final class dev/gitlive/firebase/internal/_encodersKt { - public static final fun structureEncoder (Ldev/gitlive/firebase/internal/FirebaseEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)Ldev/gitlive/firebase/internal/FirebaseCompositeEncoder; -} - diff --git a/firebase-common-internal/api/jvm/firebase-common-internal.api b/firebase-common-internal/api/jvm/firebase-common-internal.api index 44393d3b9..49e83fd95 100644 --- a/firebase-common-internal/api/jvm/firebase-common-internal.api +++ b/firebase-common-internal/api/jvm/firebase-common-internal.api @@ -78,59 +78,9 @@ public final class dev/gitlive/firebase/internal/EncodersKt { public static final fun encode (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Z)Ljava/lang/Object; public static final fun encodeAsObject (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/internal/EncodedObject; public static synthetic fun encodeAsObject$default (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/internal/EncodedObject; - public static final fun withSerializer (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)Ljava/lang/Object; -} - -public final class dev/gitlive/firebase/internal/FirebaseClassDecoder : dev/gitlive/firebase/internal/FirebaseCompositeDecoder { - public fun (ILdev/gitlive/firebase/DecodeSettings;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V - public fun decodeElementIndex (Lkotlinx/serialization/descriptors/SerialDescriptor;)I - public fun decodeSequentially ()Z -} - -public class dev/gitlive/firebase/internal/FirebaseCompositeDecoder : kotlinx/serialization/encoding/CompositeDecoder { - public fun (ILdev/gitlive/firebase/DecodeSettings;Lkotlin/jvm/functions/Function2;)V - public fun decodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z - public fun decodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)B - public fun decodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)C - public fun decodeCollectionSize (Lkotlinx/serialization/descriptors/SerialDescriptor;)I - public fun decodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)D - public fun decodeElementIndex (Lkotlinx/serialization/descriptors/SerialDescriptor;)I - public fun decodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)F - public fun decodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Decoder; - public fun decodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)I - public fun decodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)J - public fun decodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; - public fun decodeSequentially ()Z - public fun decodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/DeserializationStrategy;Ljava/lang/Object;)Ljava/lang/Object; - public fun decodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)S - public fun decodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String; - public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V - public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; -} - -public class dev/gitlive/firebase/internal/FirebaseCompositeEncoder : kotlinx/serialization/encoding/CompositeEncoder { - public fun (Ldev/gitlive/firebase/EncodeSettings;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;)V - public synthetic fun (Ldev/gitlive/firebase/EncodeSettings;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun encodeBooleanElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IZ)V - public fun encodeByteElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IB)V - public fun encodeCharElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IC)V - public fun encodeDoubleElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ID)V - public fun encodeFloatElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IF)V - public fun encodeInlineElement (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/Encoder; - public fun encodeIntElement (Lkotlinx/serialization/descriptors/SerialDescriptor;II)V - public fun encodeLongElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IJ)V - public fun encodeNullableSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V - public final fun encodeObject (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/Object;)V - public final fun encodePolymorphicClassDiscriminator (Ljava/lang/String;Ljava/lang/String;)V - public fun encodeSerializableElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V - public fun encodeShortElement (Lkotlinx/serialization/descriptors/SerialDescriptor;IS)V - public fun encodeStringElement (Lkotlinx/serialization/descriptors/SerialDescriptor;ILjava/lang/String;)V - public fun endStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)V - public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; - public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z } -public final class dev/gitlive/firebase/internal/FirebaseDecoder : kotlinx/serialization/encoding/Decoder { +public final class dev/gitlive/firebase/internal/FirebaseDecoderImpl : dev/gitlive/firebase/FirebaseDecoder { public fun (Ljava/lang/Object;)V public fun (Ljava/lang/Object;Ldev/gitlive/firebase/DecodeSettings;)V public fun beginStructure (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/encoding/CompositeDecoder; @@ -153,7 +103,7 @@ public final class dev/gitlive/firebase/internal/FirebaseDecoder : kotlinx/seria public final fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/internal/FirebaseEncoder : kotlinx/serialization/encoding/Encoder { +public final class dev/gitlive/firebase/internal/FirebaseEncoderImpl : dev/gitlive/firebase/FirebaseEncoder { public fun (Ldev/gitlive/firebase/EncodeSettings;)V public fun (Z)V public fun beginCollection (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; @@ -217,25 +167,3 @@ public final class dev/gitlive/firebase/internal/SpecialValueSerializer : kotlin public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } -public final class dev/gitlive/firebase/internal/ValueWithSerializer { - public fun (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)V - public final fun component1 ()Ljava/lang/Object; - public final fun component2 ()Lkotlinx/serialization/SerializationStrategy; - public final fun copy (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)Ldev/gitlive/firebase/internal/ValueWithSerializer; - public static synthetic fun copy$default (Ldev/gitlive/firebase/internal/ValueWithSerializer;Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;ILjava/lang/Object;)Ldev/gitlive/firebase/internal/ValueWithSerializer; - public fun equals (Ljava/lang/Object;)Z - public final fun getSerializer ()Lkotlinx/serialization/SerializationStrategy; - public final fun getValue ()Ljava/lang/Object; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class dev/gitlive/firebase/internal/_decodersKt { - public static final fun getPolymorphicType (Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String; - public static final fun structureDecoder (Ldev/gitlive/firebase/internal/FirebaseDecoder;Lkotlinx/serialization/descriptors/SerialDescriptor;Z)Lkotlinx/serialization/encoding/CompositeDecoder; -} - -public final class dev/gitlive/firebase/internal/_encodersKt { - public static final fun structureEncoder (Ldev/gitlive/firebase/internal/FirebaseEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)Ldev/gitlive/firebase/internal/FirebaseCompositeEncoder; -} - diff --git a/firebase-common-internal/src/androidMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt b/firebase-common-internal/src/androidMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt index 77644ccfe..4b477364c 100644 --- a/firebase-common-internal/src/androidMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt +++ b/firebase-common-internal/src/androidMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlinx.serialization.encoding.CompositeDecoder -public actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder = when (descriptor.kind) { +internal actual fun FirebaseDecoderImpl.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder = when (descriptor.kind) { StructureKind.CLASS, StructureKind.OBJECT -> decodeAsMap(false) StructureKind.LIST -> (value as? List<*>).orEmpty().let { FirebaseCompositeDecoder(it.size, settings) { _, index -> it[index] } @@ -26,10 +26,10 @@ public actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } -public actual fun getPolymorphicType(value: Any?, discriminator: String): String = +internal actual fun getPolymorphicType(value: Any?, discriminator: String): String = (value as? Map<*, *>).orEmpty()[discriminator] as String -private fun FirebaseDecoder.decodeAsMap(isNestedPolymorphic: Boolean): CompositeDecoder = (value as? Map<*, *>).orEmpty().let { map -> +private fun FirebaseDecoderImpl.decodeAsMap(isNestedPolymorphic: Boolean): CompositeDecoder = (value as? Map<*, *>).orEmpty().let { map -> FirebaseClassDecoder(map.size, settings, { map.containsKey(it) }) { desc, index -> if (isNestedPolymorphic) { if (desc.getElementName(index) == "value") { diff --git a/firebase-common-internal/src/androidMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt b/firebase-common-internal/src/androidMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt index e8d3aca10..eb7be3ba2 100644 --- a/firebase-common-internal/src/androidMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt +++ b/firebase-common-internal/src/androidMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.collections.set -public actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when (descriptor.kind) { +internal actual fun FirebaseEncoderImpl.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when (descriptor.kind) { StructureKind.LIST -> mutableListOf() .also { value = it } .let { FirebaseCompositeEncoder(settings) { _, index, value -> it.add(index, value) } } @@ -20,7 +20,7 @@ public actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor) else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } -private fun FirebaseEncoder.encodeAsMap(descriptor: SerialDescriptor): FirebaseCompositeEncoder = mutableMapOf() +private fun FirebaseEncoderImpl.encodeAsMap(descriptor: SerialDescriptor): FirebaseCompositeEncoder = mutableMapOf() .also { value = it } .let { FirebaseCompositeEncoder( diff --git a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/Polymorphic.kt b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/Polymorphic.kt index 77bb77dc7..2297be79b 100644 --- a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/Polymorphic.kt +++ b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/Polymorphic.kt @@ -12,7 +12,7 @@ import kotlinx.serialization.internal.AbstractPolymorphicSerializer * See https://github.com/Kotlin/kotlinx.serialization/blob/master/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt */ @Suppress("UNCHECKED_CAST") -internal fun FirebaseEncoder.encodePolymorphically( +internal fun FirebaseEncoderImpl.encodePolymorphically( serializer: SerializationStrategy, value: T, ifPolymorphic: (String) -> Unit, @@ -34,7 +34,7 @@ internal fun FirebaseEncoder.encodePolymorphically( } @Suppress("UNCHECKED_CAST") -internal fun FirebaseDecoder.decodeSerializableValuePolymorphic( +internal fun FirebaseDecoderImpl.decodeSerializableValuePolymorphic( value: Any?, deserializer: DeserializationStrategy, ): T { diff --git a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/decoders.kt b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/decoders.kt index 084bc4f62..27952ac7d 100644 --- a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/decoders.kt +++ b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/decoders.kt @@ -5,6 +5,7 @@ package dev.gitlive.firebase.internal import dev.gitlive.firebase.DecodeSettings +import dev.gitlive.firebase.FirebaseDecoder import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerializationException @@ -31,12 +32,13 @@ public inline fun decode(strategy: DeserializationStrategy, value: Any?, @PublishedApi internal fun decode(strategy: DeserializationStrategy, value: Any?, decodeSettings: DecodeSettings): T { require(value != null || strategy.descriptor.isNullable) { "Value was null for non-nullable type ${strategy.descriptor.serialName}" } - return FirebaseDecoder(value, decodeSettings).decodeSerializableValue(strategy) + return FirebaseDecoderImpl(value, decodeSettings).decodeSerializableValue(strategy) } -public expect fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder -public expect fun getPolymorphicType(value: Any?, discriminator: String): String +internal expect fun FirebaseDecoderImpl.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder +internal expect fun getPolymorphicType(value: Any?, discriminator: String): String -public class FirebaseDecoder(public val value: Any?, internal val settings: DecodeSettings) : Decoder { +@PublishedApi +internal class FirebaseDecoderImpl(val value: Any?, internal val settings: DecodeSettings) : FirebaseDecoder { public constructor(value: Any?) : this(value, DecodeSettingsImpl()) @@ -68,12 +70,12 @@ public class FirebaseDecoder(public val value: Any?, internal val settings: Deco override fun decodeNull(): Nothing? = decodeNull(value) - override fun decodeInline(descriptor: SerialDescriptor): Decoder = FirebaseDecoder(value, settings) + override fun decodeInline(descriptor: SerialDescriptor): Decoder = FirebaseDecoderImpl(value, settings) override fun decodeSerializableValue(deserializer: DeserializationStrategy): T = decodeSerializableValuePolymorphic(value, deserializer) } -public class FirebaseClassDecoder( +internal class FirebaseClassDecoder( size: Int, settings: DecodeSettings, private val containsKey: (name: String) -> Boolean, @@ -91,7 +93,7 @@ public class FirebaseClassDecoder( ?: DECODE_DONE } -public open class FirebaseCompositeDecoder( +internal open class FirebaseCompositeDecoder( private val size: Int, internal val settings: DecodeSettings, private val get: (descriptor: SerialDescriptor, index: Int) -> Any?, @@ -111,7 +113,7 @@ public open class FirebaseCompositeDecoder( deserializer: DeserializationStrategy, previousValue: T?, ): T = decodeElement(descriptor, index) { - deserializer.deserialize(FirebaseDecoder(it, settings)) + deserializer.deserialize(FirebaseDecoderImpl(it, settings)) } override fun decodeBooleanElement(descriptor: SerialDescriptor, index: Int): Boolean = @@ -160,7 +162,7 @@ public open class FirebaseCompositeDecoder( @ExperimentalSerializationApi override fun decodeInlineElement(descriptor: SerialDescriptor, index: Int): Decoder = decodeElement(descriptor, index) { - FirebaseDecoder(it, settings) + FirebaseDecoderImpl(it, settings) } private fun decodeElement(descriptor: SerialDescriptor, index: Int, decoder: (Any?) -> T): T = try { diff --git a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt index c6c9e4abf..767688ad0 100644 --- a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt +++ b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt @@ -5,6 +5,7 @@ package dev.gitlive.firebase.internal import dev.gitlive.firebase.EncodeSettings +import dev.gitlive.firebase.FirebaseEncoder import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.descriptors.SerialDescriptor @@ -22,7 +23,7 @@ public inline fun encode(strategy: SerializationStrategy, value: T, build @PublishedApi internal fun encode(strategy: SerializationStrategy, value: T, encodeSettings: EncodeSettings): Any? = - FirebaseEncoder(encodeSettings).apply { encodeSerializableValue(strategy, value) }.value + FirebaseEncoderImpl(encodeSettings).apply { encodeSerializableValue(strategy, value) }.value @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("encode(value) { this.encodeDefaults = shouldEncodeElementDefault }")) public inline fun encode(value: T, shouldEncodeElementDefault: Boolean): Any? = encode(value) { @@ -60,31 +61,17 @@ public inline fun encodeAsObject(value: T, buildSettings: Enco @PublishedApi internal inline fun encode(value: T, encodeSettings: EncodeSettings): Any? = value?.let { - FirebaseEncoder(encodeSettings).apply { - if (it is ValueWithSerializer<*> && it.value is T) { - @Suppress("UNCHECKED_CAST") - (it as ValueWithSerializer).let { - encodeSerializableValue(it.serializer, it.value) - } - } else { - encodeSerializableValue(it.firebaseSerializer(), it) - } + FirebaseEncoderImpl(encodeSettings).apply { + encodeSerializableValue(it.firebaseSerializer(), it) }.value } -/** - * An extension which which serializer to use for value. Handy in updating fields by name or path - * where using annotation is not possible - * @return a value with a custom serializer. - */ -public fun T.withSerializer(serializer: SerializationStrategy): Any = ValueWithSerializer(this, serializer) -public data class ValueWithSerializer(val value: T, val serializer: SerializationStrategy) - -public expect fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder +internal expect fun FirebaseEncoderImpl.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder -public class FirebaseEncoder( +@PublishedApi +internal class FirebaseEncoderImpl( internal val settings: EncodeSettings, -) : Encoder { +) : FirebaseEncoder { public constructor(shouldEncodeElementDefault: Boolean) : this( EncodeSettingsImpl.Builder().apply { this.encodeDefaults = shouldEncodeElementDefault }.buildEncodeSettings(), @@ -162,19 +149,13 @@ public class FirebaseEncoder( } } -public open class FirebaseCompositeEncoder( +internal open class FirebaseCompositeEncoder( private val settings: EncodeSettings, private val end: () -> Unit = {}, private val setPolymorphicType: (String, String) -> Unit = { _, _ -> }, private val set: (descriptor: SerialDescriptor, index: Int, value: Any?) -> Unit, ) : CompositeEncoder { -// private fun SerializationStrategy.toFirebase(): SerializationStrategy = when(descriptor.kind) { -// StructureKind.MAP -> FirebaseMapSerializer(descriptor.getElementDescriptor(1)) as SerializationStrategy -// StructureKind.LIST -> FirebaseListSerializer(descriptor.getElementDescriptor(0)) as SerializationStrategy -// else -> this -// } - override val serializersModule: SerializersModule = settings.serializersModule override fun endStructure(descriptor: SerialDescriptor): Unit = end() @@ -190,7 +171,7 @@ public open class FirebaseCompositeEncoder( descriptor, index, value?.let { - FirebaseEncoder(settings).apply { + FirebaseEncoderImpl(settings).apply { encodeSerializableValue(serializer, value) }.value }, @@ -204,7 +185,7 @@ public open class FirebaseCompositeEncoder( ): Unit = set( descriptor, index, - FirebaseEncoder(settings).apply { + FirebaseEncoderImpl(settings).apply { encodeSerializableValue(serializer, value) }.value, ) @@ -231,7 +212,7 @@ public open class FirebaseCompositeEncoder( @ExperimentalSerializationApi override fun encodeInlineElement(descriptor: SerialDescriptor, index: Int): Encoder = - FirebaseEncoder(settings) + FirebaseEncoderImpl(settings) public fun encodePolymorphicClassDiscriminator(discriminator: String, type: String) { setPolymorphicType(discriminator, type) diff --git a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/serializers.kt b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/serializers.kt index 41f319f88..cb1b1fb00 100644 --- a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/serializers.kt +++ b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/serializers.kt @@ -128,14 +128,14 @@ public class SpecialValueSerializer( override val descriptor: SerialDescriptor = buildClassSerialDescriptor(serialName) { } override fun serialize(encoder: Encoder, value: T) { - if (encoder is FirebaseEncoder) { + if (encoder is FirebaseEncoderImpl) { encoder.value = toNativeValue(value) } else { throw SerializationException("This serializer must be used with FirebaseEncoder") } } - override fun deserialize(decoder: Decoder): T = if (decoder is FirebaseDecoder) { + override fun deserialize(decoder: Decoder): T = if (decoder is FirebaseDecoderImpl) { fromNativeValue(decoder.value) } else { throw SerializationException("This serializer must be used with FirebaseDecoder") diff --git a/firebase-common-internal/src/commonTest/kotlin/dev/gitlive/firebase/internal/EncodersTest.kt b/firebase-common-internal/src/commonTest/kotlin/dev/gitlive/firebase/internal/EncodersTest.kt index 145d57b74..2017f8dc6 100644 --- a/firebase-common-internal/src/commonTest/kotlin/dev/gitlive/firebase/internal/EncodersTest.kt +++ b/firebase-common-internal/src/commonTest/kotlin/dev/gitlive/firebase/internal/EncodersTest.kt @@ -262,6 +262,47 @@ class EncodersTest { assertEquals(nestedClass, decoded) } + @Test + fun encodeDecodeNestedClassWithEmptyCollections() { + val module = SerializersModule { + polymorphic(AbstractClass::class, AbstractClass.serializer()) { + subclass(ImplementedClass::class, ImplementedClass.serializer()) + } + } + + val testData = TestData(mapOf(), mapOf(), true, null, ValueClass(42)) + val sealedClass: SealedClass = SealedClass.Test("value") + val abstractClass: AbstractClass = ImplementedClass("value", true) + val nestedClass = NestedClass(testData, sealedClass, abstractClass, listOf(), listOf(), listOf(), mapOf(), mapOf(), mapOf()) + val encoded = encode(NestedClass.serializer(), nestedClass) { + encodeDefaults = true + serializersModule = module + } + + val testDataEncoded = nativeMapOf("map" to nativeMapOf(), "otherMap" to nativeMapOf(), "bool" to true, "nullableBool" to null, "valueClass" to 42) + val sealedEncoded = nativeMapOf("type" to "test", "value" to "value") + val abstractEncoded = nativeMapOf("type" to "implemented", "abstractValue" to "value", "otherValue" to true) + nativeAssertEquals( + nativeMapOf( + "testData" to testDataEncoded, + "sealed" to sealedEncoded, + "abstract" to abstractEncoded, + "testDataList" to nativeListOf(), + "sealedList" to nativeListOf(), + "abstractList" to nativeListOf(), + "testDataMap" to nativeMapOf(), + "sealedMap" to nativeMapOf(), + "abstractMap" to nativeMapOf(), + ), + encoded, + ) + + val decoded = decode(NestedClass.serializer(), encoded) { + serializersModule = module + } + assertEquals(nestedClass, decoded) + } + @Test fun reencodeTransformationList() { val reencoded = reencodeTransformation>(nativeListOf("One", "Two", "Three")) { diff --git a/firebase-common-internal/src/iosMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt b/firebase-common-internal/src/iosMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt index 00400b622..561d4c762 100644 --- a/firebase-common-internal/src/iosMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt +++ b/firebase-common-internal/src/iosMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.descriptors.PolymorphicKind import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind -public actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder = when (descriptor.kind) { +internal actual fun FirebaseDecoderImpl.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder = when (descriptor.kind) { StructureKind.CLASS, StructureKind.OBJECT -> decodeAsMap(false) StructureKind.LIST -> decodeAsList() StructureKind.MAP -> (value as? Map<*, *>).orEmpty().entries.toList().let { @@ -19,13 +19,13 @@ public actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } -public actual fun getPolymorphicType(value: Any?, discriminator: String): String = +internal actual fun getPolymorphicType(value: Any?, discriminator: String): String = (value as? Map<*, *>).orEmpty()[discriminator] as String -private fun FirebaseDecoder.decodeAsList(): CompositeDecoder = (value as? List<*>).orEmpty().let { +private fun FirebaseDecoderImpl.decodeAsList(): CompositeDecoder = (value as? List<*>).orEmpty().let { FirebaseCompositeDecoder(it.size, settings) { _, index -> it[index] } } -private fun FirebaseDecoder.decodeAsMap(isNestedPolymorphic: Boolean): CompositeDecoder = (value as? Map<*, *>).orEmpty().let { map -> +private fun FirebaseDecoderImpl.decodeAsMap(isNestedPolymorphic: Boolean): CompositeDecoder = (value as? Map<*, *>).orEmpty().let { map -> FirebaseClassDecoder(map.size, settings, { map.containsKey(it) }) { desc, index -> if (isNestedPolymorphic) { if (desc.getElementName(index) == "value") { diff --git a/firebase-common-internal/src/iosMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt b/firebase-common-internal/src/iosMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt index efd200c20..bab44db4e 100644 --- a/firebase-common-internal/src/iosMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt +++ b/firebase-common-internal/src/iosMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.collections.set -public actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when (descriptor.kind) { +internal actual fun FirebaseEncoderImpl.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when (descriptor.kind) { StructureKind.LIST -> encodeAsList() StructureKind.MAP -> mutableListOf() .let { FirebaseCompositeEncoder(settings, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } @@ -18,10 +18,10 @@ public actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor) else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } -private fun FirebaseEncoder.encodeAsList(): FirebaseCompositeEncoder = mutableListOf() +private fun FirebaseEncoderImpl.encodeAsList(): FirebaseCompositeEncoder = mutableListOf() .also { value = it } .let { FirebaseCompositeEncoder(settings) { _, index, value -> it.add(index, value) } } -private fun FirebaseEncoder.encodeAsMap(descriptor: SerialDescriptor): FirebaseCompositeEncoder = mutableMapOf() +private fun FirebaseEncoderImpl.encodeAsMap(descriptor: SerialDescriptor): FirebaseCompositeEncoder = mutableMapOf() .also { value = it } .let { FirebaseCompositeEncoder( diff --git a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt index 05701df40..1787c1cc4 100644 --- a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt +++ b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt @@ -11,7 +11,7 @@ import kotlinx.serialization.descriptors.StructureKind import kotlinx.serialization.encoding.CompositeDecoder import kotlin.js.Json -public actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder = when (descriptor.kind) { +internal actual fun FirebaseDecoderImpl.structureDecoder(descriptor: SerialDescriptor, polymorphicIsNested: Boolean): CompositeDecoder = when (descriptor.kind) { StructureKind.CLASS, StructureKind.OBJECT -> decodeAsMap(false) StructureKind.LIST -> decodeAsList() StructureKind.MAP -> (js("Object").entries(value) as Array>).let { @@ -39,15 +39,15 @@ public actual fun FirebaseDecoder.structureDecoder(descriptor: SerialDescriptor, } @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") -public actual fun getPolymorphicType(value: Any?, discriminator: String): String = +internal actual fun getPolymorphicType(value: Any?, discriminator: String): String = (value as Json)[discriminator] as String -private fun FirebaseDecoder.decodeAsList(): CompositeDecoder = (value as Array<*>).let { +private fun FirebaseDecoderImpl.decodeAsList(): CompositeDecoder = (value as Array<*>).let { FirebaseCompositeDecoder(it.size, settings) { _, index -> it[index] } } @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") -private fun FirebaseDecoder.decodeAsMap(isNestedPolymorphic: Boolean): CompositeDecoder = (value as Json).let { json -> +private fun FirebaseDecoderImpl.decodeAsMap(isNestedPolymorphic: Boolean): CompositeDecoder = (value as Json).let { json -> FirebaseClassDecoder(js("Object").keys(value).length as Int, settings, { json[it] != undefined }) { desc, index -> if (isNestedPolymorphic) { if (desc.getElementName(index) == "value") { diff --git a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt index 1e21549a0..091d9c2da 100644 --- a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt +++ b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.StructureKind import kotlin.js.json -public actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when (descriptor.kind) { +internal actual fun FirebaseEncoderImpl.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when (descriptor.kind) { StructureKind.LIST -> encodeAsList(descriptor) StructureKind.MAP -> { val map = json() @@ -28,10 +28,10 @@ public actual fun FirebaseEncoder.structureEncoder(descriptor: SerialDescriptor) else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } -private fun FirebaseEncoder.encodeAsList(descriptor: SerialDescriptor): FirebaseCompositeEncoder = Array(descriptor.elementsCount) { null } +private fun FirebaseEncoderImpl.encodeAsList(descriptor: SerialDescriptor): FirebaseCompositeEncoder = Array(descriptor.elementsCount - 1) { null } .also { value = it } .let { FirebaseCompositeEncoder(settings) { _, index, value -> it[index] = value } } -private fun FirebaseEncoder.encodeAsMap(descriptor: SerialDescriptor): FirebaseCompositeEncoder = json() +private fun FirebaseEncoderImpl.encodeAsMap(descriptor: SerialDescriptor): FirebaseCompositeEncoder = json() .also { value = it } .let { FirebaseCompositeEncoder( diff --git a/firebase-common/api/android/firebase-common.api b/firebase-common/api/android/firebase-common.api index 2b70a6e8a..789d376fd 100644 --- a/firebase-common/api/android/firebase-common.api +++ b/firebase-common/api/android/firebase-common.api @@ -13,6 +13,11 @@ public abstract interface class dev/gitlive/firebase/EncodeDecodeSettings { public abstract interface class dev/gitlive/firebase/EncodeDecodeSettingsBuilder : dev/gitlive/firebase/DecodeSettings$Builder, dev/gitlive/firebase/EncodeSettings$Builder { } +public final class dev/gitlive/firebase/EncodeDecodeSettingsKt { + public static final fun copyFrom (Ldev/gitlive/firebase/DecodeSettings$Builder;Ldev/gitlive/firebase/DecodeSettings$Builder;)V + public static final fun copyFrom (Ldev/gitlive/firebase/EncodeSettings$Builder;Ldev/gitlive/firebase/EncodeSettings$Builder;)V +} + public abstract interface class dev/gitlive/firebase/EncodeSettings : dev/gitlive/firebase/EncodeDecodeSettings { public abstract fun getEncodeDefaults ()Z } @@ -33,3 +38,21 @@ public synthetic class dev/gitlive/firebase/FirebaseClassDiscriminator$Impl : de public final synthetic fun discriminator ()Ljava/lang/String; } +public abstract interface class dev/gitlive/firebase/FirebaseDecoder : kotlinx/serialization/encoding/Decoder { +} + +public final class dev/gitlive/firebase/FirebaseDecoder$DefaultImpls { + public static fun decodeNullableSerializableValue (Ldev/gitlive/firebase/FirebaseDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; + public static fun decodeSerializableValue (Ldev/gitlive/firebase/FirebaseDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +} + +public abstract interface class dev/gitlive/firebase/FirebaseEncoder : kotlinx/serialization/encoding/Encoder { +} + +public final class dev/gitlive/firebase/FirebaseEncoder$DefaultImpls { + public static fun beginCollection (Ldev/gitlive/firebase/FirebaseEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; + public static fun encodeNotNullMark (Ldev/gitlive/firebase/FirebaseEncoder;)V + public static fun encodeNullableSerializableValue (Ldev/gitlive/firebase/FirebaseEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public static fun encodeSerializableValue (Ldev/gitlive/firebase/FirebaseEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V +} + diff --git a/firebase-common/api/jvm/firebase-common.api b/firebase-common/api/jvm/firebase-common.api index 2b70a6e8a..789d376fd 100644 --- a/firebase-common/api/jvm/firebase-common.api +++ b/firebase-common/api/jvm/firebase-common.api @@ -13,6 +13,11 @@ public abstract interface class dev/gitlive/firebase/EncodeDecodeSettings { public abstract interface class dev/gitlive/firebase/EncodeDecodeSettingsBuilder : dev/gitlive/firebase/DecodeSettings$Builder, dev/gitlive/firebase/EncodeSettings$Builder { } +public final class dev/gitlive/firebase/EncodeDecodeSettingsKt { + public static final fun copyFrom (Ldev/gitlive/firebase/DecodeSettings$Builder;Ldev/gitlive/firebase/DecodeSettings$Builder;)V + public static final fun copyFrom (Ldev/gitlive/firebase/EncodeSettings$Builder;Ldev/gitlive/firebase/EncodeSettings$Builder;)V +} + public abstract interface class dev/gitlive/firebase/EncodeSettings : dev/gitlive/firebase/EncodeDecodeSettings { public abstract fun getEncodeDefaults ()Z } @@ -33,3 +38,21 @@ public synthetic class dev/gitlive/firebase/FirebaseClassDiscriminator$Impl : de public final synthetic fun discriminator ()Ljava/lang/String; } +public abstract interface class dev/gitlive/firebase/FirebaseDecoder : kotlinx/serialization/encoding/Decoder { +} + +public final class dev/gitlive/firebase/FirebaseDecoder$DefaultImpls { + public static fun decodeNullableSerializableValue (Ldev/gitlive/firebase/FirebaseDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; + public static fun decodeSerializableValue (Ldev/gitlive/firebase/FirebaseDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object; +} + +public abstract interface class dev/gitlive/firebase/FirebaseEncoder : kotlinx/serialization/encoding/Encoder { +} + +public final class dev/gitlive/firebase/FirebaseEncoder$DefaultImpls { + public static fun beginCollection (Ldev/gitlive/firebase/FirebaseEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder; + public static fun encodeNotNullMark (Ldev/gitlive/firebase/FirebaseEncoder;)V + public static fun encodeNullableSerializableValue (Ldev/gitlive/firebase/FirebaseEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public static fun encodeSerializableValue (Ldev/gitlive/firebase/FirebaseEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V +} + diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/EncodeDecodeSettings.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/EncodeDecodeSettings.kt index d73b9ede0..23f8456cc 100644 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/EncodeDecodeSettings.kt +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/EncodeDecodeSettings.kt @@ -27,6 +27,11 @@ public interface EncodeSettings : EncodeDecodeSettings { } } +public fun EncodeSettings.Builder.copyFrom(other: EncodeSettings.Builder) { + encodeDefaults = other.encodeDefaults + serializersModule = other.serializersModule +} + /** * [EncodeDecodeSettings] used when decoding an object * @param serializersModule the [SerializersModule] to use for deserialization. This allows for polymorphic serialization on runtime @@ -38,6 +43,10 @@ public interface DecodeSettings : EncodeDecodeSettings { } } +public fun DecodeSettings.Builder.copyFrom(other: DecodeSettings.Builder) { + serializersModule = other.serializersModule +} + public interface EncodeDecodeSettingsBuilder : EncodeSettings.Builder, DecodeSettings.Builder diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseDecoder.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseDecoder.kt new file mode 100644 index 000000000..2dbf07b44 --- /dev/null +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseDecoder.kt @@ -0,0 +1,5 @@ +package dev.gitlive.firebase + +import kotlinx.serialization.encoding.Decoder + +public interface FirebaseDecoder : Decoder diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseEncoder.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseEncoder.kt new file mode 100644 index 000000000..f33f69fbb --- /dev/null +++ b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/FirebaseEncoder.kt @@ -0,0 +1,5 @@ +package dev.gitlive.firebase + +import kotlinx.serialization.encoding.Encoder + +public interface FirebaseEncoder : Encoder diff --git a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt index db1f7f344..e393d118b 100644 --- a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt +++ b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt @@ -1,7 +1,7 @@ package dev.gitlive.firebase.database -import dev.gitlive.firebase.internal.FirebaseDecoder -import dev.gitlive.firebase.internal.FirebaseEncoder +import dev.gitlive.firebase.FirebaseEncoder +import dev.gitlive.firebase.FirebaseDecoder import dev.gitlive.firebase.internal.SpecialValueSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable diff --git a/firebase-firestore/api/android/firebase-firestore.api b/firebase-firestore/api/android/firebase-firestore.api index 8a0aa2e9a..acdfb6336 100644 --- a/firebase-firestore/api/android/firebase-firestore.api +++ b/firebase-firestore/api/android/firebase-firestore.api @@ -72,12 +72,11 @@ public final class dev/gitlive/firebase/firestore/DocumentReference { public final fun update (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun updateEncoded (Ldev/gitlive/firebase/internal/EncodedObject;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateEncodedFieldPathsAndValues (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateEncodedFieldsAndValues (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFieldPaths ([Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun updateFieldPaths ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun updateFieldPaths$default (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public final fun updateFields (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFields ([Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun updateFields ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun updateFields$default (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } public final class dev/gitlive/firebase/firestore/DocumentReference$Companion { @@ -170,6 +169,29 @@ public final class dev/gitlive/firebase/firestore/FieldValueSerializer : kotlinx public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } +public final class dev/gitlive/firebase/firestore/FieldValuesDSL : dev/gitlive/firebase/EncodeSettings$Builder { + public final fun addWithStrategy (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public fun getEncodeDefaults ()Z + public final fun getFieldValuesToAdd ()Ljava/util/List; + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; + public fun setEncodeDefaults (Z)V + public fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V + public final fun withEncodeSettings (Lkotlin/jvm/functions/Function1;)V +} + +public final class dev/gitlive/firebase/firestore/FieldsAndValuesUpdateDSL : dev/gitlive/firebase/EncodeSettings$Builder { + public fun getEncodeDefaults ()Z + public final fun getFieldAndValueToAdd ()Ljava/util/List; + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; + public fun setEncodeDefaults (Z)V + public fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V + public final fun to (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun to (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun toEncoded (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/internal/FieldAndValue$WithFieldPath; + public final fun toEncoded (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/internal/FieldAndValue$WithStringField; + public final fun withEncodeSettings (Lkotlin/jvm/functions/Function1;)V +} + public abstract class dev/gitlive/firebase/firestore/Filter { } @@ -182,6 +204,7 @@ public final class dev/gitlive/firebase/firestore/Filter$And : dev/gitlive/fireb } public final class dev/gitlive/firebase/firestore/Filter$Field : dev/gitlive/firebase/firestore/Filter$WithConstraint { + public fun (Ljava/lang/String;Ldev/gitlive/firebase/firestore/WhereConstraint;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ldev/gitlive/firebase/firestore/WhereConstraint; public fun equals (Ljava/lang/Object;)Z @@ -200,6 +223,7 @@ public final class dev/gitlive/firebase/firestore/Filter$Or : dev/gitlive/fireba } public final class dev/gitlive/firebase/firestore/Filter$Path : dev/gitlive/firebase/firestore/Filter$WithConstraint { + public fun (Ldev/gitlive/firebase/firestore/FieldPath;Ldev/gitlive/firebase/firestore/WhereConstraint;)V public final fun component1 ()Ldev/gitlive/firebase/firestore/FieldPath; public final fun component2 ()Ldev/gitlive/firebase/firestore/WhereConstraint; public fun equals (Ljava/lang/Object;)Z @@ -213,31 +237,40 @@ public abstract class dev/gitlive/firebase/firestore/Filter$WithConstraint : dev public abstract fun getConstraint ()Ldev/gitlive/firebase/firestore/WhereConstraint; } -public final class dev/gitlive/firebase/firestore/FilterBuilder { +public final class dev/gitlive/firebase/firestore/FilterBuilder : dev/gitlive/firebase/EncodeSettings$Builder { public final fun all ([Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter; public final fun and (Ldev/gitlive/firebase/firestore/Filter;Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter$And; public final fun any ([Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter; - public final fun contains (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun contains (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun containsAny (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun containsAny (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun equalTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun equalTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThan (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThan (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThanOrEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun inArray (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun inArray (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThan (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThan (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThanOrEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notInArray (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notInArray (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun contains (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun contains (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun containsAny (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun containsAny (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun equalTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun equalTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public fun getEncodeDefaults ()Z + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; + public final fun greaterThan (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThan (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThanOrEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun inArray (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun inArray (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNotNull (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNotNull (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNull (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNull (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThan (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThan (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThanOrEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notInArray (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notInArray (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; public final fun or (Ldev/gitlive/firebase/firestore/Filter;Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter$Or; + public fun setEncodeDefaults (Z)V + public fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V + public final fun withEncoder (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Filter; } public final class dev/gitlive/firebase/firestore/FirebaseFirestore { @@ -333,12 +366,6 @@ public final class dev/gitlive/firebase/firestore/GeoPointSerializer : kotlinx/s public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } -public final class dev/gitlive/firebase/firestore/HelpersKt { - public static final fun encodeFieldAndValue ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun performUpdateFieldPaths ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun performUpdateFields ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ljava/util/List; -} - public abstract interface class dev/gitlive/firebase/firestore/LocalCacheSettings { } @@ -425,8 +452,12 @@ public class dev/gitlive/firebase/firestore/Query { public static final field Companion Ldev/gitlive/firebase/firestore/Query$Companion; public final fun endAt (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; public final fun endAt ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun endAt ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun endAtFieldValues (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; public final fun endBefore (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; public final fun endBefore ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun endBefore ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun endBeforeFieldValues (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; public final fun get (Ldev/gitlive/firebase/firestore/Source;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun get$default (Ldev/gitlive/firebase/firestore/Query;Ldev/gitlive/firebase/firestore/Source;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun getSnapshots ()Lkotlinx/coroutines/flow/Flow; @@ -439,8 +470,12 @@ public class dev/gitlive/firebase/firestore/Query { public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/Query;ZILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; public final fun startAfter (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; public final fun startAfter ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAfter ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAfterFieldValues (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; public final fun startAt (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; public final fun startAt ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAt ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAtFieldValues (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; public final fun where (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; } @@ -561,12 +596,11 @@ public final class dev/gitlive/firebase/firestore/Transaction { public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; public final fun updateEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateEncodedFieldPathsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateEncodedFieldsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; - public static synthetic fun updateFieldPaths$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; - public static synthetic fun updateFields$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; } public final class dev/gitlive/firebase/firestore/Transaction$Companion { @@ -576,117 +610,92 @@ public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint { } public final class dev/gitlive/firebase/firestore/WhereConstraint$ArrayContains : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$ArrayContainsAny : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { - public final fun component1 ()Ljava/util/List; + public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$EqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForArray : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValues ()Ljava/util/List; public abstract fun getValues ()Ljava/util/List; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForArray$DefaultImpls { - public static fun getSafeValues (Ldev/gitlive/firebase/firestore/WhereConstraint$ForArray;)Ljava/util/List; -} - public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValue ()Ljava/lang/Object; public abstract fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject$DefaultImpls { - public static fun getSafeValue (Ldev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject;)Ljava/lang/Object; -} - public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForObject : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValue ()Ljava/lang/Object; public abstract fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForObject$DefaultImpls { - public static fun getSafeValue (Ldev/gitlive/firebase/firestore/WhereConstraint$ForObject;)Ljava/lang/Object; -} - public final class dev/gitlive/firebase/firestore/WhereConstraint$GreaterThan : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$GreaterThanOrEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$InArray : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { - public final fun component1 ()Ljava/util/List; + public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$LessThan : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$LessThanOrEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$NotEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$NotInArray : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { - public final fun component1 ()Ljava/util/List; + public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -714,12 +723,11 @@ public final class dev/gitlive/firebase/firestore/WriteBatch { public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Z)Ldev/gitlive/firebase/firestore/WriteBatch; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun updateEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateEncodedFieldPathsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateEncodedFieldsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; - public static synthetic fun updateField$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; - public static synthetic fun updateFieldPath$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; } public final class dev/gitlive/firebase/firestore/WriteBatch$Companion { diff --git a/firebase-firestore/api/jvm/firebase-firestore.api b/firebase-firestore/api/jvm/firebase-firestore.api index 33a64aef7..1019894f5 100644 --- a/firebase-firestore/api/jvm/firebase-firestore.api +++ b/firebase-firestore/api/jvm/firebase-firestore.api @@ -72,12 +72,11 @@ public final class dev/gitlive/firebase/firestore/DocumentReference { public final fun update (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun updateEncoded (Ldev/gitlive/firebase/internal/EncodedObject;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateEncodedFieldPathsAndValues (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateEncodedFieldsAndValues (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFieldPaths ([Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun updateFieldPaths ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun updateFieldPaths$default (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public final fun updateFields (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFields ([Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun updateFields ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun updateFields$default (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } public final class dev/gitlive/firebase/firestore/DocumentReference$Companion { @@ -170,6 +169,29 @@ public final class dev/gitlive/firebase/firestore/FieldValueSerializer : kotlinx public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } +public final class dev/gitlive/firebase/firestore/FieldValuesDSL : dev/gitlive/firebase/EncodeSettings$Builder { + public final fun addWithStrategy (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public fun getEncodeDefaults ()Z + public final fun getFieldValuesToAdd ()Ljava/util/List; + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; + public fun setEncodeDefaults (Z)V + public fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V + public final fun withEncodeSettings (Lkotlin/jvm/functions/Function1;)V +} + +public final class dev/gitlive/firebase/firestore/FieldsAndValuesUpdateDSL : dev/gitlive/firebase/EncodeSettings$Builder { + public fun getEncodeDefaults ()Z + public final fun getFieldAndValueToAdd ()Ljava/util/List; + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; + public fun setEncodeDefaults (Z)V + public fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V + public final fun to (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun to (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun toEncoded (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/internal/FieldAndValue$WithFieldPath; + public final fun toEncoded (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/internal/FieldAndValue$WithStringField; + public final fun withEncodeSettings (Lkotlin/jvm/functions/Function1;)V +} + public abstract class dev/gitlive/firebase/firestore/Filter { } @@ -182,6 +204,7 @@ public final class dev/gitlive/firebase/firestore/Filter$And : dev/gitlive/fireb } public final class dev/gitlive/firebase/firestore/Filter$Field : dev/gitlive/firebase/firestore/Filter$WithConstraint { + public fun (Ljava/lang/String;Ldev/gitlive/firebase/firestore/WhereConstraint;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ldev/gitlive/firebase/firestore/WhereConstraint; public fun equals (Ljava/lang/Object;)Z @@ -200,6 +223,7 @@ public final class dev/gitlive/firebase/firestore/Filter$Or : dev/gitlive/fireba } public final class dev/gitlive/firebase/firestore/Filter$Path : dev/gitlive/firebase/firestore/Filter$WithConstraint { + public fun (Ldev/gitlive/firebase/firestore/FieldPath;Ldev/gitlive/firebase/firestore/WhereConstraint;)V public final fun component1 ()Ldev/gitlive/firebase/firestore/FieldPath; public final fun component2 ()Ldev/gitlive/firebase/firestore/WhereConstraint; public fun equals (Ljava/lang/Object;)Z @@ -213,31 +237,40 @@ public abstract class dev/gitlive/firebase/firestore/Filter$WithConstraint : dev public abstract fun getConstraint ()Ldev/gitlive/firebase/firestore/WhereConstraint; } -public final class dev/gitlive/firebase/firestore/FilterBuilder { +public final class dev/gitlive/firebase/firestore/FilterBuilder : dev/gitlive/firebase/EncodeSettings$Builder { public final fun all ([Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter; public final fun and (Ldev/gitlive/firebase/firestore/Filter;Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter$And; public final fun any ([Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter; - public final fun contains (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun contains (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun containsAny (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun containsAny (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun equalTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun equalTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThan (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThan (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThanOrEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun inArray (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun inArray (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThan (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThan (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThanOrEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notInArray (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notInArray (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun contains (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun contains (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun containsAny (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun containsAny (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun equalTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun equalTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public fun getEncodeDefaults ()Z + public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule; + public final fun greaterThan (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThan (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThanOrEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun inArray (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun inArray (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNotNull (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNotNull (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNull (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNull (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThan (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThan (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThanOrEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notInArray (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notInArray (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; public final fun or (Ldev/gitlive/firebase/firestore/Filter;Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter$Or; + public fun setEncodeDefaults (Z)V + public fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V + public final fun withEncoder (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Filter; } public final class dev/gitlive/firebase/firestore/FirebaseFirestore { @@ -333,12 +366,6 @@ public final class dev/gitlive/firebase/firestore/GeoPointSerializer : kotlinx/s public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } -public final class dev/gitlive/firebase/firestore/HelpersKt { - public static final fun encodeFieldAndValue ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun performUpdateFieldPaths ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun performUpdateFields ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ljava/util/List; -} - public abstract interface class dev/gitlive/firebase/firestore/LocalCacheSettings { } @@ -425,8 +452,12 @@ public class dev/gitlive/firebase/firestore/Query { public static final field Companion Ldev/gitlive/firebase/firestore/Query$Companion; public final fun endAt (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; public final fun endAt ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun endAt ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun endAtFieldValues (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; public final fun endBefore (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; public final fun endBefore ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun endBefore ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun endBeforeFieldValues (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; public final fun get (Ldev/gitlive/firebase/firestore/Source;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun get$default (Ldev/gitlive/firebase/firestore/Query;Ldev/gitlive/firebase/firestore/Source;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun getSnapshots ()Lkotlinx/coroutines/flow/Flow; @@ -439,8 +470,12 @@ public class dev/gitlive/firebase/firestore/Query { public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/Query;ZILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; public final fun startAfter (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; public final fun startAfter ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAfter ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAfterFieldValues (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; public final fun startAt (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; public final fun startAt ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAt ([Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAtFieldValues (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; public final fun where (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; } @@ -561,12 +596,11 @@ public final class dev/gitlive/firebase/firestore/Transaction { public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; public final fun updateEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateEncodedFieldPathsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateEncodedFieldsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; - public static synthetic fun updateFieldPaths$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; - public static synthetic fun updateFields$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; } public final class dev/gitlive/firebase/firestore/Transaction$Companion { @@ -576,117 +610,92 @@ public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint { } public final class dev/gitlive/firebase/firestore/WhereConstraint$ArrayContains : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$ArrayContainsAny : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { - public final fun component1 ()Ljava/util/List; + public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$EqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForArray : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValues ()Ljava/util/List; public abstract fun getValues ()Ljava/util/List; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForArray$DefaultImpls { - public static fun getSafeValues (Ldev/gitlive/firebase/firestore/WhereConstraint$ForArray;)Ljava/util/List; -} - public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValue ()Ljava/lang/Object; public abstract fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject$DefaultImpls { - public static fun getSafeValue (Ldev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject;)Ljava/lang/Object; -} - public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForObject : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValue ()Ljava/lang/Object; public abstract fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForObject$DefaultImpls { - public static fun getSafeValue (Ldev/gitlive/firebase/firestore/WhereConstraint$ForObject;)Ljava/lang/Object; -} - public final class dev/gitlive/firebase/firestore/WhereConstraint$GreaterThan : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$GreaterThanOrEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$InArray : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { - public final fun component1 ()Ljava/util/List; + public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$LessThan : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$LessThanOrEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$NotEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject { - public final fun component1 ()Ljava/lang/Object; + public fun (Lkotlin/jvm/functions/Function0;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$NotInArray : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { - public final fun component1 ()Ljava/util/List; + public fun (Ljava/util/List;)V public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -714,12 +723,11 @@ public final class dev/gitlive/firebase/firestore/WriteBatch { public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Z)Ldev/gitlive/firebase/firestore/WriteBatch; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun updateEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateEncodedFieldPathsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateEncodedFieldsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; - public static synthetic fun updateField$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; - public static synthetic fun updateFieldPath$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; } public final class dev/gitlive/firebase/firestore/WriteBatch$Companion { diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index cb91ffd74..2cc2047a0 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -2,7 +2,6 @@ package dev.gitlive.firebase.firestore.internal import com.google.android.gms.tasks.TaskExecutors import com.google.firebase.firestore.MetadataChanges -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot import dev.gitlive.firebase.firestore.Source @@ -44,19 +43,12 @@ internal actual class NativeDocumentReference actual constructor(actual val nati android.update(encodedData.android).await() } - actual suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) { + actual suspend fun updateEncoded(encodedFieldsAndValues: List) { encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() }?.let { - android.update(encodedFieldsAndValues.toMap()) + encodedFieldsAndValues.performUpdate(android::update, android::update) }?.await() } - actual suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) { - encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() } - ?.performUpdate { field, value, moreFieldsAndValues -> - android.update(field, value, *moreFieldsAndValues) - }?.await() - } - actual suspend fun delete() { android.delete().await() } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt index 5dc17f72b..7505e09ef 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt @@ -59,7 +59,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.EqualTo -> com.google.firebase.firestore.Filter::equalTo is WhereConstraint.NotEqualTo -> com.google.firebase.firestore.Filter::notEqualTo } - modifier.invoke(field, constraint.safeValue) + modifier.invoke(field, constraint.value) } is WhereConstraint.ForObject -> { val modifier: (String, Any) -> com.google.firebase.firestore.Filter = when (constraint) { @@ -69,7 +69,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.GreaterThanOrEqualTo -> com.google.firebase.firestore.Filter::greaterThanOrEqualTo is WhereConstraint.ArrayContains -> com.google.firebase.firestore.Filter::arrayContains } - modifier.invoke(field, constraint.safeValue) + modifier.invoke(field, constraint.value) } is WhereConstraint.ForArray -> { val modifier: (String, List) -> com.google.firebase.firestore.Filter = when (constraint) { @@ -77,7 +77,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.ArrayContainsAny -> com.google.firebase.firestore.Filter::arrayContainsAny is WhereConstraint.NotInArray -> com.google.firebase.firestore.Filter::notInArray } - modifier.invoke(field, constraint.safeValues) + modifier.invoke(field, constraint.values) } } } @@ -88,7 +88,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.EqualTo -> com.google.firebase.firestore.Filter::equalTo is WhereConstraint.NotEqualTo -> com.google.firebase.firestore.Filter::notEqualTo } - modifier.invoke(path.android, constraint.safeValue) + modifier.invoke(path.android, constraint.value) } is WhereConstraint.ForObject -> { val modifier: (FieldPath, Any) -> com.google.firebase.firestore.Filter = when (constraint) { @@ -98,7 +98,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.GreaterThanOrEqualTo -> com.google.firebase.firestore.Filter::greaterThanOrEqualTo is WhereConstraint.ArrayContains -> com.google.firebase.firestore.Filter::arrayContains } - modifier.invoke(path.android, constraint.safeValue) + modifier.invoke(path.android, constraint.value) } is WhereConstraint.ForArray -> { val modifier: (FieldPath, List) -> com.google.firebase.firestore.Filter = when (constraint) { @@ -106,7 +106,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.ArrayContainsAny -> com.google.firebase.firestore.Filter::arrayContainsAny is WhereConstraint.NotInArray -> com.google.firebase.firestore.Filter::notInArray } - modifier.invoke(path.android, constraint.safeValues) + modifier.invoke(path.android, constraint.values) } } } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt index 4b8219703..ea20d3f33 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeTransaction import dev.gitlive.firebase.firestore.android import dev.gitlive.firebase.firestore.performUpdate @@ -23,19 +22,17 @@ internal actual class NativeTransactionWrapper internal actual constructor(actua actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject) = native.update(documentRef.android, encodedData.android).let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ) = encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - native.update(documentRef.android, field, value, *moreFieldsAndValues) - }.let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ) = encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - native.update(documentRef.android, field, value, *moreFieldsAndValues) - }.let { this } + encodedFieldsAndValues: List, + ) = encodedFieldsAndValues.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + native.update(documentRef.android, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + native.update(documentRef.android, fieldPath, value, *moreFieldsAndValues) + }, + ).let { this } actual fun delete(documentRef: DocumentReference) = native.delete(documentRef.android).let { this } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt index 9aa34f700..1a3bd69b9 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeWriteBatch import dev.gitlive.firebase.firestore.android import dev.gitlive.firebase.firestore.performUpdate @@ -25,19 +24,17 @@ internal actual class NativeWriteBatchWrapper internal actual constructor(actual actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject) = native.update(documentRef.android, encodedData.android).let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ) = encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - native.update(documentRef.android, field, value, *moreFieldsAndValues) - }.let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ) = encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - native.update(documentRef.android, field, value, *moreFieldsAndValues) - }.let { this } + encodedFieldsAndValues: List, + ) = encodedFieldsAndValues.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + native.update(documentRef.android, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + native.update(documentRef.android, fieldPath, value, *moreFieldsAndValues) + }, + ).let { this } actual fun delete(documentRef: DocumentReference) = native.delete(documentRef.android).let { this } diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceSerializer.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceSerializer.kt index 945230bc7..10fa9341f 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceSerializer.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceSerializer.kt @@ -1,7 +1,7 @@ package dev.gitlive.firebase.firestore +import dev.gitlive.firebase.FirebaseEncoder import dev.gitlive.firebase.firestore.internal.NativeDocumentReference -import dev.gitlive.firebase.internal.FirebaseEncoder import dev.gitlive.firebase.internal.SpecialValueSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValueSerializer.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValueSerializer.kt index 561dcb201..d2717474d 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValueSerializer.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValueSerializer.kt @@ -1,6 +1,6 @@ package dev.gitlive.firebase.firestore -import dev.gitlive.firebase.internal.FirebaseEncoder +import dev.gitlive.firebase.FirebaseEncoder import dev.gitlive.firebase.internal.SpecialValueSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValuesDSL.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValuesDSL.kt new file mode 100644 index 000000000..7c1f4d4a7 --- /dev/null +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValuesDSL.kt @@ -0,0 +1,60 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.EncodeSettings +import dev.gitlive.firebase.copyFrom +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.modules.SerializersModule + +/** + * A builder for generating the field values of a [Query]. + * The order of the field values must match the order by clauses of the [Query] + */ +public class FieldValuesDSL internal constructor() : EncodeSettings.Builder { + + override var encodeDefaults: Boolean = true + override var serializersModule: SerializersModule = EmptySerializersModule() + + @PublishedApi + internal val fieldValuesToAdd: MutableList<() -> Any> = mutableListOf() + internal val fieldValues get() = fieldValuesToAdd.map { valueToEncode -> + valueToEncode.invoke() + } + + /** + * Adds a field value to the [Query] + * The [value] will be encoded according to the [EncodeSettings] set by this builder. + * @param T the type of the value to add + * @param value the value [T] to add + */ + public inline fun add(value: T) { + fieldValuesToAdd.add { + encode(value, { copyFrom(this@FieldValuesDSL) })!! + } + } + + /** + * Adds a field value to the [Query] + * The [value] will be encoded according to the [EncodeSettings] set by this builder. + * @param T the type of the value to add + * @param strategy the [SerializationStrategy] to apply to the value + * @param value the value [T] to add + */ + public fun addWithStrategy(strategy: SerializationStrategy, value: T) { + fieldValuesToAdd.add { + dev.gitlive.firebase.internal.encode(strategy, value, { copyFrom(this@FieldValuesDSL) })!! + } + } + + /** + * Provides an accessor for encoding values with [EncodeSettings] + * @param dls the [FieldValuesDSL] to specify the [EncodeSettings] and values to add + */ + public fun withEncodeSettings(dls: FieldValuesDSL.() -> Unit) { + fieldValuesToAdd.addAll( + FieldValuesDSL() + .apply { copyFrom(this@FieldValuesDSL) } + .apply(dls).fieldValuesToAdd, + ) + } +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldsAndValuesUpdateDSL.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldsAndValuesUpdateDSL.kt new file mode 100644 index 000000000..5ea2ae37d --- /dev/null +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldsAndValuesUpdateDSL.kt @@ -0,0 +1,104 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.EncodeSettings +import dev.gitlive.firebase.copyFrom +import dev.gitlive.firebase.firestore.internal.FieldAndValue +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.modules.SerializersModule + +/** + * A builder for generating a collection of updates to a document. + * Updates can be applied to either a String field or to a [FieldPath]. + * Within this builder custom serialization can be applied to the update. + * + * ``` + * val update: FieldsAndValuesUpdateBuilder.() -> Unit = { + * "path" to 1 + * FieldPath("subpath", "field") to "value" + * "otherPath".to(strategy, value) + * } + * ``` + */ +public class FieldsAndValuesUpdateDSL internal constructor() : EncodeSettings.Builder { + + override var encodeDefaults: Boolean = true + override var serializersModule: SerializersModule = EmptySerializersModule() + + @PublishedApi + internal val fieldAndValueToAdd: MutableList<() -> FieldAndValue> = mutableListOf() + internal val fieldsAndValues: List get() = fieldAndValueToAdd.map { fieldAndValueToEncode -> + fieldAndValueToEncode.invoke() + } + + /** + * Updates the field represented by a String to a given value + * The [value] will be encoded according to the [EncodeSettings] set by this builder. + * @param T the type of the value + * @param value the value [T] to update to + */ + public inline infix fun String.to(value: T) { + fieldAndValueToAdd.add { + toEncoded(encode(value, { copyFrom(this@FieldsAndValuesUpdateDSL) })) + } + } + + /** + * Updates a [FieldPath] to a given value + * The [value] will be encoded according to the [EncodeSettings] set by this builder. + * @param T the type of the value + * @param value the value [T] to update to + */ + public inline infix fun FieldPath.to(value: T) { + fieldAndValueToAdd.add { + toEncoded(encode(value, { copyFrom(this@FieldsAndValuesUpdateDSL) })) + } + } + + /** + * Updates the field represented by a String to a given value + * The [value] will be encoded according to the [EncodeSettings] set by this builder. + * @param T the type of the value + * @param strategy the [SerializationStrategy] to apply to the value + * @param value the value [T] to update to + */ + public fun String.to(strategy: SerializationStrategy, value: T) { + fieldAndValueToAdd.add { + toEncoded(dev.gitlive.firebase.internal.encode(strategy, value, { copyFrom(this@FieldsAndValuesUpdateDSL) })) + } + } + + /** + * Updates a [FieldPath] to a given value + * The [value] will be encoded according to the [EncodeSettings] set by this builder. + * @param T the type of the value + * @param strategy the [SerializationStrategy] to apply to the value + * @param value the value [T] to update to + */ + public fun FieldPath.to(strategy: SerializationStrategy, value: T) { + fieldAndValueToAdd.add { + toEncoded(dev.gitlive.firebase.internal.encode(strategy, value, { copyFrom(this@FieldsAndValuesUpdateDSL) })) + } + } + + /** + * Provides an accessor for encoding values with [EncodeSettings] + * @param dls the [WithEncoder] to specify the [EncodeSettings] and values to add + */ + public fun withEncodeSettings(dls: FieldsAndValuesUpdateDSL.() -> Unit) { + fieldAndValueToAdd.addAll( + FieldsAndValuesUpdateDSL() + .apply { copyFrom(this@FieldsAndValuesUpdateDSL) } + .apply(dls) + .fieldAndValueToAdd, + ) + } + + @PublishedApi + internal fun String.toEncoded(encodedValue: Any?): FieldAndValue.WithStringField = + FieldAndValue.WithStringField(this, encodedValue) + + @PublishedApi + internal fun FieldPath.toEncoded(encodedValue: Any?): FieldAndValue.WithFieldPath = + FieldAndValue.WithFieldPath(this, encodedValue) +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/Filter.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/Filter.kt index f9e8558e7..ec745f517 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/Filter.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/Filter.kt @@ -1,87 +1,372 @@ package dev.gitlive.firebase.firestore -import dev.gitlive.firebase.firestore.internal.safeValue +import dev.gitlive.firebase.EncodeSettings +import dev.gitlive.firebase.copyFrom +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.modules.SerializersModule public sealed interface WhereConstraint { public sealed interface ForNullableObject : WhereConstraint { public val value: Any? - public val safeValue: Any? get() = value?.safeValue } public sealed interface ForObject : WhereConstraint { public val value: Any - public val safeValue: Any get() = value.safeValue } + public sealed interface ForArray : WhereConstraint { public val values: List - public val safeValues: List get() = values.map { it.safeValue } - } - - public data class EqualTo internal constructor(override val value: Any?) : ForNullableObject - public data class NotEqualTo internal constructor(override val value: Any?) : ForNullableObject - public data class LessThan internal constructor(override val value: Any) : ForObject - public data class GreaterThan internal constructor(override val value: Any) : ForObject - public data class LessThanOrEqualTo internal constructor(override val value: Any) : ForObject - public data class GreaterThanOrEqualTo internal constructor(override val value: Any) : ForObject - public data class ArrayContains internal constructor(override val value: Any) : ForObject - public data class ArrayContainsAny internal constructor(override val values: List) : ForArray - public data class InArray internal constructor(override val values: List) : ForArray - public data class NotInArray internal constructor(override val values: List) : ForArray -} - -public sealed class Filter { - public data class And internal constructor(val filters: List) : Filter() - public data class Or internal constructor(val filters: List) : Filter() - public sealed class WithConstraint : Filter() { - public abstract val constraint: WhereConstraint } - public data class Field internal constructor(val field: String, override val constraint: WhereConstraint) : WithConstraint() - public data class Path internal constructor(val path: FieldPath, override val constraint: WhereConstraint) : WithConstraint() -} - -public class FilterBuilder internal constructor() { - - public infix fun String.equalTo(value: Any?): Filter.WithConstraint = Filter.Field(this, WhereConstraint.EqualTo(value)) - - public infix fun FieldPath.equalTo(value: Any?): Filter.WithConstraint = Filter.Path(this, WhereConstraint.EqualTo(value)) - - public infix fun String.notEqualTo(value: Any?): Filter.WithConstraint = Filter.Field(this, WhereConstraint.NotEqualTo(value)) - - public infix fun FieldPath.notEqualTo(value: Any?): Filter.WithConstraint = Filter.Path(this, WhereConstraint.NotEqualTo(value)) - - public infix fun String.lessThan(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.LessThan(value)) - - public infix fun FieldPath.lessThan(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.LessThan(value)) - - public infix fun String.greaterThan(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.GreaterThan(value)) - - public infix fun FieldPath.greaterThan(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.GreaterThan(value)) + public data class EqualTo @PublishedApi internal constructor(private val valueBuilder: () -> Any?) : ForNullableObject { + override val value: Any? get() = valueBuilder() + } - public infix fun String.lessThanOrEqualTo(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.LessThanOrEqualTo(value)) + public data class NotEqualTo @PublishedApi internal constructor(private val valueBuilder: () -> Any?) : ForNullableObject { + override val value: Any? get() = valueBuilder() + } - public infix fun FieldPath.lessThanOrEqualTo(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.LessThanOrEqualTo(value)) + public data class LessThan @PublishedApi internal constructor(private val valueBuilder: () -> Any) : ForObject { + override val value: Any get() = valueBuilder() + } - public infix fun String.greaterThanOrEqualTo(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.GreaterThanOrEqualTo(value)) + public data class GreaterThan @PublishedApi internal constructor(private val valueBuilder: () -> Any) : ForObject { + override val value: Any get() = valueBuilder() + } - public infix fun FieldPath.greaterThanOrEqualTo(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.GreaterThanOrEqualTo(value)) + public data class LessThanOrEqualTo @PublishedApi internal constructor(private val valueBuilder: () -> Any) : ForObject { + override val value: Any get() = valueBuilder() + } - public infix fun String.contains(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.ArrayContains(value)) + public data class GreaterThanOrEqualTo @PublishedApi internal constructor(private val valueBuilder: () -> Any) : ForObject { + override val value: Any get() = valueBuilder() + } - public infix fun FieldPath.contains(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.ArrayContains(value)) + public data class ArrayContains @PublishedApi internal constructor(private val valueBuilder: () -> Any) : ForObject { + override val value: Any get() = valueBuilder() + } - public infix fun String.containsAny(values: List): Filter.WithConstraint = Filter.Field(this, WhereConstraint.ArrayContainsAny(values)) + public data class ArrayContainsAny @PublishedApi internal constructor(private val valueBuilders: List<() -> Any>) : ForArray { + override val values: List get() = valueBuilders.map { it.invoke() } + } - public infix fun FieldPath.containsAny(values: List): Filter.WithConstraint = Filter.Path(this, WhereConstraint.ArrayContainsAny(values)) + public data class InArray @PublishedApi internal constructor(private val valueBuilders: List<() -> Any>) : ForArray { + override val values: List get() = valueBuilders.map { it.invoke() } + } - public infix fun String.inArray(values: List): Filter.WithConstraint = Filter.Field(this, WhereConstraint.InArray(values)) + public data class NotInArray @PublishedApi internal constructor(private val valueBuilders: List<() -> Any>) : ForArray { + override val values: List get() = valueBuilders.map { it.invoke() } + } +} - public infix fun FieldPath.inArray(values: List): Filter.WithConstraint = Filter.Path(this, WhereConstraint.InArray(values)) +public sealed class Filter { + public data class And internal constructor(val filters: List) : Filter() + public data class Or internal constructor(val filters: List) : Filter() + public sealed class WithConstraint : Filter() { + public abstract val constraint: WhereConstraint + } - public infix fun String.notInArray(values: List): Filter.WithConstraint = Filter.Field(this, WhereConstraint.NotInArray(values)) + public data class Field @PublishedApi internal constructor(val field: String, override val constraint: WhereConstraint) : WithConstraint() + public data class Path @PublishedApi internal constructor(val path: FieldPath, override val constraint: WhereConstraint) : WithConstraint() +} - public infix fun FieldPath.notInArray(values: List): Filter.WithConstraint = Filter.Path(this, WhereConstraint.NotInArray(values)) +public class FilterBuilder internal constructor() : EncodeSettings.Builder { + + override var encodeDefaults: Boolean = true + override var serializersModule: SerializersModule = EmptySerializersModule() + + public fun withEncoder(dsl: FilterBuilder.() -> Filter): Filter = FilterBuilder() + .run(dsl) + + public val String.isNull: Filter.WithConstraint get() = Filter.Field(this, WhereConstraint.EqualTo { null }) + public inline infix fun String.equalTo(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.EqualTo { + encode(value) { copyFrom(this@FilterBuilder) } + }, + ) + public fun String.equalTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.EqualTo { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) } + }, + ) + + public val FieldPath.isNull: Filter.WithConstraint get() = Filter.Path(this, WhereConstraint.EqualTo { null }) + public inline infix fun FieldPath.equalTo(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.EqualTo { + encode(value) { copyFrom(this@FilterBuilder) } + }, + ) + public fun FieldPath.equalTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.EqualTo { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) } + }, + ) + + public val String.isNotNull: Filter.WithConstraint get() = Filter.Field(this, WhereConstraint.NotEqualTo { null }) + public inline infix fun String.notEqualTo(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.NotEqualTo { + encode(value) { copyFrom(this@FilterBuilder) } + }, + ) + public fun String.notEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.NotEqualTo { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) } + }, + ) + + public val FieldPath.isNotNull: Filter.WithConstraint get() = Filter.Path(this, WhereConstraint.NotEqualTo { null }) + public inline infix fun FieldPath.notEqualTo(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.NotEqualTo { + encode(value) { copyFrom(this@FilterBuilder) } + }, + ) + public fun FieldPath.notEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.NotEqualTo { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) } + }, + ) + + public inline infix fun String.lessThan(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.LessThan { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun String.lessThan(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.LessThan { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun FieldPath.lessThan(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.LessThan { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun FieldPath.lessThan(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.LessThan { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun String.greaterThan(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.GreaterThan { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun String.greaterThan(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.GreaterThan { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun FieldPath.greaterThan(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.GreaterThan { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun FieldPath.greaterThan(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.GreaterThan { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun String.lessThanOrEqualTo(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.LessThanOrEqualTo { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun String.lessThanOrEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.LessThanOrEqualTo { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun FieldPath.lessThanOrEqualTo(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.LessThanOrEqualTo { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun FieldPath.lessThanOrEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.LessThanOrEqualTo { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun String.greaterThanOrEqualTo(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.GreaterThanOrEqualTo { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun String.greaterThanOrEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.GreaterThanOrEqualTo { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun FieldPath.greaterThanOrEqualTo(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.GreaterThanOrEqualTo { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun FieldPath.greaterThanOrEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.GreaterThanOrEqualTo { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun String.contains(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.ArrayContains { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun String.contains(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.ArrayContains { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun FieldPath.contains(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.ArrayContains { + encode(value) { copyFrom(this@FilterBuilder) }!! + }, + ) + public fun FieldPath.contains(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.ArrayContains { + dev.gitlive.firebase.internal.encode(strategy, value) { copyFrom(this@FilterBuilder) }!! + }, + ) + + public inline infix fun String.containsAny(values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.ArrayContainsAny( + values.map { value -> + { encode(value) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + public fun String.containsAny(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.ArrayContainsAny( + values.map { + { dev.gitlive.firebase.internal.encode(strategy, it) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + + public inline infix fun FieldPath.containsAny(values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.ArrayContainsAny( + values.map { value -> + { encode(value) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + public fun FieldPath.containsAny(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.ArrayContainsAny( + values.map { + { dev.gitlive.firebase.internal.encode(strategy, it) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + + public inline infix fun String.inArray(values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.InArray( + values.map { value -> + { encode(value) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + public fun String.inArray(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.InArray( + values.map { + { dev.gitlive.firebase.internal.encode(strategy, it) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + + public inline infix fun FieldPath.inArray(values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.InArray( + values.map { value -> + { encode(value) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + public fun FieldPath.inArray(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.InArray( + values.map { + { dev.gitlive.firebase.internal.encode(strategy, it) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + + public inline infix fun String.notInArray(values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.NotInArray( + values.map { value -> + { encode(value) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + public fun String.notInArray(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.NotInArray( + values.map { + { dev.gitlive.firebase.internal.encode(strategy, it) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + + public inline infix fun FieldPath.notInArray(values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.NotInArray( + values.map { value -> + { encode(value) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) + public fun FieldPath.notInArray(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.NotInArray( + values.map { + { dev.gitlive.firebase.internal.encode(strategy, it) { copyFrom(this@FilterBuilder) }!! } + }, + ), + ) public infix fun Filter.and(right: Filter): Filter.And { val leftList = when (this) { diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/GeoPointSerializer.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/GeoPointSerializer.kt index cb46e1792..7228704bc 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/GeoPointSerializer.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/GeoPointSerializer.kt @@ -1,5 +1,6 @@ package dev.gitlive.firebase.firestore +import dev.gitlive.firebase.FirebaseEncoder import dev.gitlive.firebase.internal.SpecialValueSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/TimestampSerializer.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/TimestampSerializer.kt index f970b0801..791e4d6f5 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/TimestampSerializer.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/TimestampSerializer.kt @@ -1,5 +1,7 @@ package dev.gitlive.firebase.firestore +import dev.gitlive.firebase.FirebaseEncoder +import dev.gitlive.firebase.FirebaseDecoder import dev.gitlive.firebase.internal.SpecialValueSerializer import dev.gitlive.firebase.firestore.DoubleAsTimestampSerializer.SERVER_TIMESTAMP import kotlinx.serialization.KSerializer diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index bf4723e49..660e4fcd9 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -18,7 +18,6 @@ import dev.gitlive.firebase.firestore.internal.NativeQueryWrapper import dev.gitlive.firebase.firestore.internal.NativeTransactionWrapper import dev.gitlive.firebase.firestore.internal.NativeWriteBatchWrapper import dev.gitlive.firebase.firestore.internal.SetOptions -import dev.gitlive.firebase.firestore.internal.safeValue import dev.gitlive.firebase.internal.decode import dev.gitlive.firebase.internal.encodeAsObject import kotlinx.coroutines.flow.Flow @@ -179,31 +178,61 @@ public data class Transaction internal constructor(internal val nativeWrapper: N internal fun setEncoded(documentRef: DocumentReference, encodedData: EncodedObject, setOptions: SetOptions): Transaction = Transaction(nativeWrapper.setEncoded(documentRef, encodedData, setOptions)) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("update(documentRef, data) { this.encodeDefaults = encodeDefaults }")) - public fun update(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean): Transaction = update(documentRef, data) { - this.encodeDefaults = encodeDefaults - } + public fun update(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean): Transaction = + update(documentRef, data) { + this.encodeDefaults = encodeDefaults + } public inline fun update(documentRef: DocumentReference, data: Any, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Transaction = updateEncoded(documentRef, encodeAsObject(data, buildSettings)) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("update(documentRef, strategy, data) { this.encodeDefaults = encodeDefaults }")) - public fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean): Transaction = update(documentRef, strategy, data) { - this.encodeDefaults = encodeDefaults - } + public fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean): Transaction = + update(documentRef, strategy, data) { + this.encodeDefaults = encodeDefaults + } public inline fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Transaction = updateEncoded(documentRef, encodeAsObject(strategy, data, buildSettings)) @JvmName("updateFields") - public inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Transaction = updateEncodedFieldsAndValues(documentRef, encodeFieldAndValue(fieldsAndValues, buildSettings).orEmpty()) + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair): Transaction = + update(documentRef, *fieldsAndValues) {} + + @JvmName("updateFields") + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit): Transaction = + updateFields( + documentRef, + ) { + apply(buildSettings) + fieldsAndValues.forEach { (field, value) -> + field to value + } + } @JvmName("updateFieldPaths") - public inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Transaction = updateEncodedFieldPathsAndValues(documentRef, encodeFieldAndValue(fieldsAndValues, buildSettings).orEmpty()) + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair): Transaction = + update(documentRef, *fieldsAndValues) {} - @PublishedApi - internal fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): Transaction = Transaction(nativeWrapper.updateEncoded(documentRef, encodedData)) + @JvmName("updateFieldPaths") + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit): Transaction = + updateFields( + documentRef, + ) { + apply(buildSettings) + fieldsAndValues.forEach { (field, value) -> + field to value + } + } - @PublishedApi - internal fun updateEncodedFieldsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): Transaction = Transaction(nativeWrapper.updateEncodedFieldsAndValues(documentRef, encodedFieldsAndValues)) + /** + * Updates Fields/[FieldPath] of a [DocumentReference] using a [FieldsAndValuesUpdateDSL]. + * @param documentRef the [DocumentReference] to update + * @param fieldsAndValuesUpdateDSL closure for configuring the [FieldsAndValuesUpdateDSL] + */ + public fun updateFields( + documentRef: DocumentReference, + fieldsAndValuesUpdateDSL: FieldsAndValuesUpdateDSL.() -> Unit, + ): Transaction = Transaction(nativeWrapper.updateEncoded(documentRef, FieldsAndValuesUpdateDSL().apply(fieldsAndValuesUpdateDSL).fieldsAndValues)) @PublishedApi - internal fun updateEncodedFieldPathsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): Transaction = Transaction(nativeWrapper.updateEncodedFieldPathsAndValues(documentRef, encodedFieldsAndValues)) + internal fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): Transaction = Transaction(nativeWrapper.updateEncoded(documentRef, encodedData)) public fun delete(documentRef: DocumentReference): Transaction = Transaction(nativeWrapper.delete(documentRef)) public suspend fun get(documentRef: DocumentReference): DocumentSnapshot = DocumentSnapshot(nativeWrapper.get(documentRef)) @@ -230,14 +259,79 @@ public open class Query internal constructor(internal val nativeQuery: NativeQue public fun orderBy(field: FieldPath, direction: Direction = Direction.ASCENDING): Query = Query(nativeQuery.orderBy(field.encoded, direction)) public fun startAfter(document: DocumentSnapshot): Query = Query(nativeQuery.startAfter(document.native)) - public fun startAfter(vararg fieldValues: Any): Query = Query(nativeQuery.startAfter(*(fieldValues.map { it.safeValue }.toTypedArray()))) + public fun startAfter(vararg fieldValues: Any?): Query = startAfter(*fieldValues) {} + public fun startAfter(vararg fieldValues: Any?, buildSettings: EncodeSettings.Builder.() -> Unit): Query = + startAfterFieldValues { + apply(buildSettings) + + fieldValues.forEach { + add(it) + } + } + + /** + * Creates and returns a new [Query] that starts after the provided fields relative to the order of the query. + * The field values are configured using a [FieldValuesDSL]. + * The order of the field values must match the order of the [orderBy] clauses of the query + * @param builder closure for configuring the [FieldValuesDSL] + */ + public fun startAfterFieldValues(builder: FieldValuesDSL.() -> Unit): Query = Query(nativeQuery.startAfter(*FieldValuesDSL().apply(builder).fieldValues.toTypedArray())) + public fun startAt(document: DocumentSnapshot): Query = Query(nativeQuery.startAt(document.native)) - public fun startAt(vararg fieldValues: Any): Query = Query(nativeQuery.startAt(*(fieldValues.map { it.safeValue }.toTypedArray()))) + public fun startAt(vararg fieldValues: Any?): Query = startAt(*fieldValues) {} + public fun startAt(vararg fieldValues: Any?, buildSettings: EncodeSettings.Builder.() -> Unit): Query = + startAtFieldValues { + apply(buildSettings) + fieldValues.forEach { + add(it) + } + } + + /** + * Creates and returns a new [Query] that starts at the provided fields relative to the order of the query. + * The field values are configured using a [FieldValuesDSL]. + * The order of the field values must match the order of the [orderBy] clauses of the query + * @param builder closure for configuring the [FieldValuesDSL] + */ + public fun startAtFieldValues(builder: FieldValuesDSL.() -> Unit): Query = Query(nativeQuery.startAt(*FieldValuesDSL().apply(builder).fieldValues.toTypedArray())) public fun endBefore(document: DocumentSnapshot): Query = Query(nativeQuery.endBefore(document.native)) - public fun endBefore(vararg fieldValues: Any): Query = Query(nativeQuery.endBefore(*(fieldValues.map { it.safeValue }.toTypedArray()))) + public fun endBefore(vararg fieldValues: Any?): Query = endBefore(*fieldValues) {} + public fun endBefore(vararg fieldValues: Any?, buildSettings: EncodeSettings.Builder.() -> Unit): Query = + endBeforeFieldValues { + withEncodeSettings { + apply(buildSettings) + fieldValues.forEach { + add(it) + } + } + } + + /** + * Creates and returns a new [Query] that ends before the provided fields relative to the order of the query. + * The field values are configured using a [FieldValuesDSL]. + * The order of the field values must match the order of the [orderBy] clauses of the query + * @param builder closure for configuring the [FieldValuesDSL] + */ + public fun endBeforeFieldValues(builder: FieldValuesDSL.() -> Unit): Query = Query(nativeQuery.endBefore(*FieldValuesDSL().apply(builder).fieldValues.toTypedArray())) + public fun endAt(document: DocumentSnapshot): Query = Query(nativeQuery.endAt(document.native)) - public fun endAt(vararg fieldValues: Any): Query = Query(nativeQuery.endAt(*(fieldValues.map { it.safeValue }.toTypedArray()))) + public fun endAt(vararg fieldValues: Any?): Query = endAt(*fieldValues) {} + public fun endAt(vararg fieldValues: Any?, buildSettings: EncodeSettings.Builder.() -> Unit): Query = + endAtFieldValues { + apply(buildSettings) + fieldValues.forEach { + add(it) + } + } + + /** + * Creates and returns a new [Query] that ends at the provided fields relative to the order of the query. + * The field values are configured using a [FieldValuesDSL]. + * The order of the field values must match the order of the [orderBy] clauses of the query + * @param builder closure for configuring the [FieldValuesDSL] + */ + public fun endAtFieldValues(builder: FieldValuesDSL.() -> Unit): Query = Query(nativeQuery.endAt(*FieldValuesDSL().apply(builder).fieldValues.toTypedArray())) } @Deprecated("Deprecated in favor of using a [FilterBuilder]", replaceWith = ReplaceWith("where { field equalTo equalTo }", "dev.gitlive.firebase.firestore")) @@ -348,33 +442,68 @@ public data class WriteBatch internal constructor(internal val nativeWrapper: Na internal fun setEncoded(documentRef: DocumentReference, encodedData: EncodedObject, setOptions: SetOptions): WriteBatch = WriteBatch(nativeWrapper.setEncoded(documentRef, encodedData, setOptions)) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("update(documentRef, data) { this.encodeDefaults = encodeDefaults }")) - public inline fun update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean): WriteBatch = update(documentRef, data) { - this.encodeDefaults = encodeDefaults - } + public inline fun update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean): WriteBatch = + update(documentRef, data) { + this.encodeDefaults = encodeDefaults + } public inline fun update(documentRef: DocumentReference, data: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): WriteBatch = updateEncoded(documentRef, encodeAsObject(data, buildSettings)) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("update(documentRef, strategy, data) { this.encodeDefaults = encodeDefaults }")) - public fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean): WriteBatch = update(documentRef, strategy, data) { - this.encodeDefaults = encodeDefaults - } + public fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean): WriteBatch = + update(documentRef, strategy, data) { + this.encodeDefaults = encodeDefaults + } public inline fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): WriteBatch = updateEncoded(documentRef, encodeAsObject(strategy, data, buildSettings)) @JvmName("updateField") - public inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}): WriteBatch = updateEncodedFieldsAndValues(documentRef, encodeFieldAndValue(fieldsAndValues, buildSettings).orEmpty()) + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair): WriteBatch = + update(documentRef, *fieldsAndValues) {} + + @JvmName("updateField") + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit): WriteBatch = + updateFields( + documentRef, + ) { + apply(buildSettings) + fieldsAndValues.forEach { (field, value) -> + field to value + } + } @JvmName("updateFieldPath") - public inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}): WriteBatch = updateEncodedFieldPathsAndValues(documentRef, encodeFieldAndValue(fieldsAndValues, buildSettings).orEmpty()) + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair): WriteBatch = + update(documentRef, *fieldsAndValues) {} - @PublishedApi - internal fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): WriteBatch = WriteBatch(nativeWrapper.updateEncoded(documentRef, encodedData)) + @JvmName("updateFieldPath") + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit): WriteBatch = + updateFields( + documentRef, + ) { + apply(buildSettings) + fieldsAndValues.forEach { (path, value) -> + path to value + } + } - @PublishedApi - internal fun updateEncodedFieldsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): WriteBatch = WriteBatch(nativeWrapper.updateEncodedFieldsAndValues(documentRef, encodedFieldsAndValues)) + /** + * Updates Fields/[FieldPath] of a [DocumentReference] using a [FieldsAndValuesUpdateDSL]. + * @param documentRef the [DocumentReference] to update + * @param fieldsAndValuesUpdateDSL closure for configuring the [FieldsAndValuesUpdateDSL] + */ + public fun updateFields( + documentRef: DocumentReference, + fieldsAndValuesUpdateDSL: FieldsAndValuesUpdateDSL.() -> Unit, + ): WriteBatch = WriteBatch( + nativeWrapper.updateEncoded( + documentRef, + FieldsAndValuesUpdateDSL().apply(fieldsAndValuesUpdateDSL).fieldsAndValues, + ), + ) @PublishedApi - internal fun updateEncodedFieldPathsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): WriteBatch = WriteBatch(nativeWrapper.updateEncodedFieldPathsAndValues(documentRef, encodedFieldsAndValues)) + internal fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): WriteBatch = WriteBatch(nativeWrapper.updateEncoded(documentRef, encodedData)) public fun delete(documentRef: DocumentReference): WriteBatch = WriteBatch(nativeWrapper.delete(documentRef)) public suspend fun commit() { @@ -513,33 +642,39 @@ public data class DocumentReference internal constructor(internal val native: Na } @JvmName("updateFields") - public suspend inline fun update(vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}) { - updateEncodedFieldsAndValues( - encodeFieldAndValue( - fieldsAndValues, - buildSettings, - ).orEmpty(), - ) - } + public suspend fun update(vararg fieldsAndValues: Pair): Unit = + update(*fieldsAndValues) {} - @PublishedApi - internal suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) { - native.updateEncodedFieldsAndValues(encodedFieldsAndValues) - } + @JvmName("updateFields") + public suspend fun update(vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit): Unit = + updateFields { + apply(buildSettings) + fieldsAndValues.forEach { (field, value) -> + field to value + } + } @JvmName("updateFieldPaths") - public suspend inline fun update(vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}) { - updateEncodedFieldPathsAndValues( - encodeFieldAndValue( - fieldsAndValues, - buildSettings, - ).orEmpty(), - ) - } + public suspend fun update(vararg fieldsAndValues: Pair): Unit = + update(*fieldsAndValues) {} - @PublishedApi - internal suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) { - native.updateEncodedFieldPathsAndValues(encodedFieldsAndValues) + @JvmName("updateFieldPaths") + public suspend fun update(vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit): Unit = + updateFields { + apply(buildSettings) + fieldsAndValues.forEach { (fieldPath, value) -> + fieldPath to value + } + } + + /** + * Updates Fields/[FieldPath] using a [FieldsAndValuesUpdateDSL]. + * @param fieldsAndValuesUpdateDSL closure for configuring the [FieldsAndValuesUpdateDSL] + */ + public suspend fun updateFields( + fieldsAndValuesUpdateDSL: FieldsAndValuesUpdateDSL.() -> Unit, + ) { + native.updateEncoded(FieldsAndValuesUpdateDSL().apply(fieldsAndValuesUpdateDSL).fieldsAndValues) } public suspend fun delete() { diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/helpers.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/helpers.kt index 766836a76..7247ca002 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/helpers.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/helpers.kt @@ -1,38 +1,39 @@ package dev.gitlive.firebase.firestore -import dev.gitlive.firebase.EncodeSettings -import kotlin.jvm.JvmName +import dev.gitlive.firebase.firestore.internal.FieldAndValue -// ** Helper method to perform an update operation. */ -@JvmName("performUpdateFields") -@PublishedApi -internal inline fun encodeFieldAndValue( - fieldsAndValues: Array>, - buildSettings: EncodeSettings.Builder.() -> Unit, -): List>? = encodeFieldAndValue(fieldsAndValues, encodeField = { it }, encodeValue = { encode(it, buildSettings) }) +internal fun List.performUpdate( + updateAsField: (String, Any?, Array) -> R, + updateAsFieldPath: (EncodedFieldPath, Any?, Array) -> R, +): R { + val first = first() + val remaining = drop(1).flatMap { fieldAndValue -> + listOf( + when (fieldAndValue) { + is FieldAndValue.WithFieldPath -> fieldAndValue.path.encoded + is FieldAndValue.WithStringField -> fieldAndValue.field + }, + fieldAndValue.value, + ) + } + return when (first) { + is FieldAndValue.WithFieldPath -> updateAsFieldPath( + first.path.encoded, + first.value, + remaining.toTypedArray(), + ) -/** Helper method to perform an update operation. */ -@JvmName("performUpdateFieldPaths") -@PublishedApi -internal inline fun encodeFieldAndValue( - fieldsAndValues: Array>, - buildSettings: EncodeSettings.Builder.() -> Unit, -): List>? = encodeFieldAndValue(fieldsAndValues, { it.encoded }, { encode(it, buildSettings) }) + is FieldAndValue.WithStringField -> updateAsField( + first.field, + first.value, + remaining.toTypedArray(), + ) + } +} -/** Helper method to perform an update operation in Android and JS. */ -@PublishedApi -internal inline fun encodeFieldAndValue( - fieldsAndValues: Array>, - encodeField: (T) -> K, - encodeValue: (Any?) -> Any?, -): List>? = - fieldsAndValues.takeUnless { fieldsAndValues.isEmpty() } - ?.map { (field, value) -> encodeField(field) to value?.let { encodeValue(it) } } - -internal fun List>.performUpdate( - update: (K, Any?, Array) -> R, -) = update( - this[0].first, - this[0].second, - this.drop(1).flatMap { (field, value) -> listOf(field, value) }.toTypedArray(), -) +internal fun List.toEncodedMap(): Map = associate { fieldAndValue -> + when (fieldAndValue) { + is FieldAndValue.WithStringField -> fieldAndValue.field to fieldAndValue.value + is FieldAndValue.WithFieldPath -> fieldAndValue.path.encoded to fieldAndValue.value + } +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/FieldAndValue.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/FieldAndValue.kt new file mode 100644 index 000000000..c4d5beeb4 --- /dev/null +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/FieldAndValue.kt @@ -0,0 +1,11 @@ +package dev.gitlive.firebase.firestore.internal + +import dev.gitlive.firebase.firestore.FieldPath + +internal sealed class FieldAndValue { + + abstract val value: Any? + + data class WithStringField(val field: String, override val value: Any?) : FieldAndValue() + data class WithFieldPath(val path: FieldPath, override val value: Any?) : FieldAndValue() +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index baa706419..7cf376cf8 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -1,6 +1,5 @@ package dev.gitlive.firebase.firestore.internal -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeCollectionReference import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot @@ -20,7 +19,6 @@ internal expect class NativeDocumentReference(nativeValue: NativeDocumentReferen suspend fun get(source: Source = Source.DEFAULT): NativeDocumentSnapshot suspend fun setEncoded(encodedData: EncodedObject, setOptions: SetOptions) suspend fun updateEncoded(encodedData: EncodedObject) - suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) - suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) + suspend fun updateEncoded(encodedFieldsAndValues: List) suspend fun delete() } diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt index 6bb72eedf..8a4df4971 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeTransaction import dev.gitlive.firebase.internal.EncodedObject @@ -11,8 +10,7 @@ internal expect class NativeTransactionWrapper internal constructor(native: Nati fun setEncoded(documentRef: DocumentReference, encodedData: EncodedObject, setOptions: SetOptions): NativeTransactionWrapper fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeTransactionWrapper - fun updateEncodedFieldsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): NativeTransactionWrapper - fun updateEncodedFieldPathsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): NativeTransactionWrapper + fun updateEncoded(documentRef: DocumentReference, encodedFieldsAndValues: List): NativeTransactionWrapper fun delete(documentRef: DocumentReference): NativeTransactionWrapper suspend fun get(documentRef: DocumentReference): NativeDocumentSnapshotWrapper } diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt index bea3d0071..fee11b836 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeWriteBatch import dev.gitlive.firebase.internal.EncodedObject @@ -9,8 +8,7 @@ internal expect class NativeWriteBatchWrapper internal constructor(native: Nativ val native: NativeWriteBatch fun setEncoded(documentRef: DocumentReference, encodedData: EncodedObject, setOptions: SetOptions): NativeWriteBatchWrapper fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeWriteBatchWrapper - fun updateEncodedFieldsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): NativeWriteBatchWrapper - fun updateEncodedFieldPathsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): NativeWriteBatchWrapper + fun updateEncoded(documentRef: DocumentReference, encodedFieldsAndValues: List): NativeWriteBatchWrapper fun delete(documentRef: DocumentReference): NativeWriteBatchWrapper suspend fun commit() } diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/SafeValue.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/SafeValue.kt deleted file mode 100644 index afca32060..000000000 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/SafeValue.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.gitlive.firebase.firestore.internal - -import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.GeoPoint -import dev.gitlive.firebase.firestore.Timestamp - -internal val Any.safeValue: Any get() = when (this) { - is Timestamp -> nativeValue - is GeoPoint -> nativeValue - is DocumentReference -> native.nativeValue - is Map<*, *> -> this.mapNotNull { (key, value) -> key?.let { it.safeValue to value?.safeValue } } - is Collection<*> -> this.mapNotNull { it?.safeValue } - else -> this -} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt new file mode 100644 index 000000000..1862dba40 --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt @@ -0,0 +1,430 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.internal.decode +import dev.gitlive.firebase.runTest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withContext +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.nullable +import kotlinx.serialization.builtins.serializer +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +@IgnoreForAndroidUnitTest +class DocumentReferenceTest : BaseFirebaseFirestoreTest() { + + @Serializable + data class FirestoreTimeTest( + val prop1: String, + val time: BaseTimestamp?, + ) + + @Serializable + data class TestDataWithDocumentReference( + val uid: String, + val reference: DocumentReference, + val optionalReference: DocumentReference?, + ) + + @Serializable + data class TestDataWithOptionalDocumentReference( + val optionalReference: DocumentReference?, + ) + + @Test + fun encodeDocumentReference() = runTest { + val doc = firestore.document("a/b") + val item = TestDataWithDocumentReference("123", doc, doc) + val encoded = encodedAsMap( + encode(item) { + encodeDefaults = false + }, + ) + assertEquals("123", encoded["uid"]) + assertEquals(doc.nativeValue, encoded["reference"]) + assertEquals(doc.nativeValue, encoded["optionalReference"]) + } + + @Test + fun encodeNullDocumentReference() = runTest { + val item = TestDataWithOptionalDocumentReference(null) + val encoded = encodedAsMap( + encode(item) { + encodeDefaults = false + }, + ) + assertNull(encoded["optionalReference"]) + } + + @Test + fun decodeDocumentReference() = runTest { + val doc = firestore.document("a/b") + val obj = mapOf( + "uid" to "123", + "reference" to doc.nativeValue, + "optionalReference" to doc.nativeValue, + ).asEncoded() + val decoded: TestDataWithDocumentReference = decode(obj) + assertEquals("123", decoded.uid) + assertEquals(doc.path, decoded.reference.path) + assertEquals(doc.path, decoded.optionalReference?.path) + } + + @Test + fun decodeNullDocumentReference() = runTest { + val obj = mapOf("optionalReference" to null).asEncoded() + val decoded: TestDataWithOptionalDocumentReference = decode(obj) + assertNull(decoded.optionalReference?.path) + } + + @Test + fun testServerTimestampFieldValue() = testDocument( + firestore + .collection("testServerTimestampFieldValue") + .document("test"), + ) { doc -> + doc.set( + FirestoreTimeTest.serializer(), + FirestoreTimeTest("ServerTimestamp", Timestamp(123, 0)), + ) + assertEquals(Timestamp(123, 0), doc.get().get("time", TimestampSerializer)) + + doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestamp", Timestamp.ServerTimestamp)) + + assertNotEquals(Timestamp.ServerTimestamp, doc.get().get("time", BaseTimestamp.serializer())) + assertNotEquals(Timestamp.ServerTimestamp, doc.get().data(FirestoreTimeTest.serializer()).time) + } + + @Test + fun testServerTimestampBehaviorNone() = testDocument( + firestore + .collection("testServerTimestampBehaviorNone") + .document("test${Random.nextInt()}"), + ) { doc -> + val deferredPendingWritesSnapshot = async { + doc.snapshots.filter { it.exists }.first() + } + nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot + + doc.set( + FirestoreTimeTest.serializer(), + FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp), + ) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNull(pendingWritesSnapshot.get("time", BaseTimestamp.serializer().nullable, serverTimestampBehavior = ServerTimestampBehavior.NONE)) + } + + @Test + fun testServerTimestampBehaviorEstimate() = testDocument( + firestore + .collection("testServerTimestampBehaviorEstimate") + .document("test${Random.nextInt()}"), + ) { doc -> + val deferredPendingWritesSnapshot = async { + doc.snapshots.filter { it.exists }.first() + } + nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot + + doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp)) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNotNull(pendingWritesSnapshot.get("time", ServerTimestampBehavior.ESTIMATE)) + assertNotEquals(Timestamp.ServerTimestamp, pendingWritesSnapshot.data(FirestoreTimeTest.serializer(), serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE).time) + } + + @Test + fun testServerTimestampBehaviorPrevious() = testDocument( + firestore + .collection("testServerTimestampBehaviorPrevious") + .document("test${Random.nextInt()}"), + ) { doc -> + val deferredPendingWritesSnapshot = async { + doc.snapshots.filter { it.exists }.first() + } + nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot + + doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp)) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNull(pendingWritesSnapshot.get("time", BaseTimestamp.serializer().nullable, serverTimestampBehavior = ServerTimestampBehavior.PREVIOUS)) + } + + @Test + fun testDocumentAutoId() = testDocument( + firestore + .collection("testDocumentAutoId") + .document, + ) { doc -> + + doc.set(FirestoreTest.serializer(), FirestoreTest("AutoId")) + + val resultDoc = firestore + .collection("testDocumentAutoId") + .document(doc.id) + .get() + + assertEquals(true, resultDoc.exists) + assertEquals("AutoId", resultDoc.get("prop1")) + } + + @Test + fun testUpdateValues() = testDocument( + firestore + .collection("testFirestoreUpdateMultipleValues") + .document("test1"), + ) { doc -> + doc.set( + FirestoreTest.serializer(), + FirestoreTest( + "property", + count = 0, + nested = NestedObject("nested"), + duration = 600.milliseconds, + ), + ) + val dataBefore = doc.get().data(FirestoreTest.serializer()) + assertEquals(0, dataBefore.count) + assertNull(dataBefore.optional) + assertEquals(NestedObject("nested"), dataBefore.nested) + assertEquals(600.milliseconds, dataBefore.duration) + + doc.updateFields { + FirestoreTest::count.name to 5 + FieldPath(FirestoreTest::optional.name) to "notNull" + FirestoreTest::nested.name.to( + NestedObject.serializer(), + NestedObject("newProperty"), + ) + FieldPath(FirestoreTest::duration.name).to( + DurationAsIntSerializer(), + 700.milliseconds, + ) + } + val dataAfter = doc.get().data(FirestoreTest.serializer()) + assertEquals(5, dataAfter.count) + assertEquals("notNull", dataAfter.optional) + assertEquals(NestedObject("newProperty"), dataAfter.nested) + assertEquals(700.milliseconds, dataAfter.duration) + } + + @Test + fun testIncrementFieldValue() = testDocument( + firestore + .collection("testFirestoreIncrementFieldValue") + .document("test1"), + ) { doc -> + doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", count = 0)) + val dataBefore = doc.get().data(FirestoreTest.serializer()) + assertEquals(0, dataBefore.count) + + doc.update("count" to FieldValue.increment(5)) + val dataAfter = doc.get().data(FirestoreTest.serializer()) + assertEquals(5, dataAfter.count) + } + + @Test + fun testArrayUnion() = testDocument( + firestore + .collection("testFirestoreArrayUnion") + .document("test1"), + ) { doc -> + doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first"))) + val dataBefore = doc.get().data(FirestoreTest.serializer()) + assertEquals(listOf("first"), dataBefore.list) + + doc.update("list" to FieldValue.arrayUnion("second")) + val dataAfter = doc.get().data(FirestoreTest.serializer()) + assertEquals(listOf("first", "second"), dataAfter.list) + } + + @Test + fun testArrayRemove() = testDocument( + firestore + .collection("testFirestoreArrayRemove") + .document("test1"), + ) { doc -> + doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first", "second"))) + val dataBefore = doc.get().data(FirestoreTest.serializer()) + assertEquals(listOf("first", "second"), dataBefore.list) + + doc.update("list" to FieldValue.arrayRemove("second")) + val dataAfter = doc.get().data(FirestoreTest.serializer()) + assertEquals(listOf("first"), dataAfter.list) + } + + @Test + fun testLegacyDoubleTimestamp() = testDocument( + firestore + .collection("testLegacyDoubleTimestamp") + .document("test${Random.nextInt()}"), + ) { doc -> + @Serializable + data class DoubleTimestamp( + @Serializable(with = DoubleAsTimestampSerializer::class) + val time: Double?, + ) + + val deferredPendingWritesSnapshot = async { + doc.snapshots.filter { it.exists }.first() + } + nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot + + doc.set(DoubleTimestamp.serializer(), DoubleTimestamp(DoubleAsTimestampSerializer.SERVER_TIMESTAMP)) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNotNull(pendingWritesSnapshot.get("time", DoubleAsTimestampSerializer, serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE)) + assertNotEquals(DoubleAsTimestampSerializer.SERVER_TIMESTAMP, pendingWritesSnapshot.data(DoubleTimestamp.serializer(), serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE).time) + } + + @Test + fun testLegacyDoubleTimestampWriteNewFormatRead() = testDocument( + firestore + .collection("testLegacyDoubleTimestampEncodeDecode") + .document("testLegacy"), + ) { doc -> + @Serializable + data class LegacyDocument( + @Serializable(with = DoubleAsTimestampSerializer::class) + val time: Double, + ) + + @Serializable + data class NewDocument( + val time: Timestamp, + ) + + val ms = 12345678.0 + + doc.set(LegacyDocument.serializer(), LegacyDocument(time = ms)) + + val fetched: NewDocument = doc.get().data(NewDocument.serializer()) + assertEquals(ms, fetched.time.toMilliseconds()) + } + + @Test + fun testGeoPointSerialization() = testDocument( + firestore.collection("geoPointSerialization") + .document("geoPointSerialization"), + ) { doc -> + @Serializable + data class DataWithGeoPoint(val geoPoint: GeoPoint) + + val data = DataWithGeoPoint(GeoPoint(12.34, 56.78)) + // store geo point + doc.set(DataWithGeoPoint.serializer(), data) + // restore data + val savedData = doc.get().data(DataWithGeoPoint.serializer()) + assertEquals(data.geoPoint, savedData.geoPoint) + + // update data + val updatedData = DataWithGeoPoint(GeoPoint(87.65, 43.21)) + doc.update(FieldPath(DataWithGeoPoint::geoPoint.name) to updatedData.geoPoint) + // verify update + val updatedSavedData = doc.get().data(DataWithGeoPoint.serializer()) + assertEquals(updatedData.geoPoint, updatedSavedData.geoPoint) + } + + @Test + fun testDocumentReferenceSerialization() = runTest { + @Serializable + data class DataWithDocumentReference( + val documentReference: DocumentReference, + ) + + val collection = firestore.collection("documentReferenceSerialization") + val document = collection + .document("documentReferenceSerialization") + val documentRef1 = collection.document("refDoc1") + val documentRef2 = collection.document("refDoc2") + + try { + documentRef1.set(mapOf("value" to 1)) + documentRef2.set(mapOf("value" to 2)) + val data = DataWithDocumentReference(documentRef1) + // store reference + document.set(DataWithDocumentReference.serializer(), data) + // restore data + val savedData = document.get().data(DataWithDocumentReference.serializer()) + assertEquals(data.documentReference.path, savedData.documentReference.path) + + // update data + val updatedData = DataWithDocumentReference(documentRef2) + document.updateFields { + FieldPath(DataWithDocumentReference::documentReference.name).to( + DocumentReferenceSerializer, + updatedData.documentReference, + ) + } + // verify update + val updatedSavedData = document.get().data(DataWithDocumentReference.serializer()) + assertEquals( + updatedData.documentReference.path, + updatedSavedData.documentReference.path, + ) + } finally { + document.delete() + documentRef1.delete() + documentRef2.delete() + } + } + + @Test + fun testFieldValuesOps() = testDocument( + firestore.collection("fieldValuesOps") + .document("fieldValuesOps"), + ) { doc -> + @Serializable + data class TestData(val values: List) + + val data = TestData(listOf(1)) + // store + doc.set(TestData.serializer(), data) + // append & verify + doc.update(FieldPath(TestData::values.name) to FieldValue.arrayUnion(2)) + + var savedData = doc.get().data(TestData.serializer()) + assertEquals(listOf(1, 2), savedData.values) + + // remove & verify + doc.update(FieldPath(TestData::values.name) to FieldValue.arrayRemove(1)) + savedData = doc.get().data(TestData.serializer()) + assertEquals(listOf(2), savedData.values) + + val list = doc.get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) + assertEquals(listOf(2), list) + // delete & verify + doc.update(FieldPath(TestData::values.name) to FieldValue.delete) + val deletedList = doc.get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) + assertNull(deletedList) + } + + private suspend fun nonSkippedDelay(timeout: Duration) = withContext(Dispatchers.Default) { + delay(timeout) + } + + private fun testDocument(document: DocumentReference, block: suspend CoroutineScope.(DocumentReference) -> Unit) = runTest { + try { + block(document) + } finally { + document.delete() + } + } +} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt index 2a5d74d90..f87664321 100644 --- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt @@ -12,7 +12,7 @@ class FirestoreSourceTest { lateinit var firestore: FirebaseFirestore companion object { - val testDoc = FirebaseFirestoreTest.FirestoreTest( + val testDoc = BaseFirebaseFirestoreTest.FirestoreTest( "aaa", 0.0, 1, @@ -21,10 +21,6 @@ class FirestoreSourceTest { ) } - private suspend fun setDoc() { - firestore.collection("testFirestoreQuerying").document("one").set(testDoc) - } - private fun initializeFirebase(persistenceEnabled: Boolean = false) { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, @@ -58,67 +54,75 @@ class FirestoreSourceTest { } @Test - fun testGetFromServer_withPersistence() = runTest { - initializeFirebase(persistenceEnabled = true) - setDoc() - val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.SERVER) + fun testGetFromServer_withPersistence() = testFirebaseDoc(true) { + val doc = get(Source.SERVER) assertTrue(doc.exists) assertFalse(doc.metadata.isFromCache) } @Test - fun testGetFromServer_withoutPersistence() = runTest { - initializeFirebase(persistenceEnabled = false) - setDoc() - val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.SERVER) + fun testGetFromServer_withoutPersistence() = testFirebaseDoc(false) { + val doc = get(Source.SERVER) assertTrue(doc.exists) assertFalse(doc.metadata.isFromCache) } @Test - fun testGetFromCache() = runTest { - initializeFirebase(persistenceEnabled = true) - + fun testGetFromCache() = testFirebaseDoc(true) { // Warm up cache by setting a document - setDoc() + set(testDoc) - val cachedDoc = firestore.collection("testFirestoreQuerying").document("one").get(Source.CACHE) + val cachedDoc = get(Source.CACHE) assertTrue(cachedDoc.exists) assertTrue(cachedDoc.metadata.isFromCache) } @Test - fun testGetFromCache_withoutPersistence() = runTest { - initializeFirebase(persistenceEnabled = false) - setDoc() + fun testGetFromCache_withoutPersistence() = testFirebaseDoc(false) { assertFailsWith(FirebaseFirestoreException::class) { - firestore.collection("testFirestoreQuerying").document("one").get(Source.CACHE) + get(Source.CACHE) } } @Test - fun testGetDefault_withPersistence() = runTest { - initializeFirebase(persistenceEnabled = false) - val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.DEFAULT) + fun testGetDefault_withPersistence() = testFirebaseDoc(false) { + val doc = get(Source.DEFAULT) assertTrue(doc.exists) assertFalse(doc.metadata.isFromCache) } @Test - fun testGet() = runTest { - initializeFirebase(persistenceEnabled = false) - val doc = firestore.collection("testFirestoreQuerying").document("one").get() + fun testGet() = testFirebaseDoc(false) { + val doc = get() assertTrue(doc.exists) assertFalse(doc.metadata.isFromCache) } @Test - fun testGetDefault_withoutPersistence() = runTest { - initializeFirebase(persistenceEnabled = true) - setDoc() - val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.DEFAULT) + fun testGetDefault_withoutPersistence() = testFirebaseDoc(true) { + val doc = get(Source.DEFAULT) assertTrue(doc.exists) // Firebase defaults to first fetching from server assertFalse(doc.metadata.isFromCache) } + + private fun testFirebaseDoc( + persistenceEnabled: Boolean, + block: suspend DocumentReference.() -> Unit, + ) = runTest { + initializeFirebase() + val doc = firestore.collection("testFirestoreQuerying").document("one") + doc.set(testDoc) + + Firebase.apps(context).forEach { it.delete() } + + initializeFirebase(persistenceEnabled = persistenceEnabled) + + val newDoc = firestore.collection("testFirestoreQuerying").document("one") + try { + newDoc.block() + } finally { + newDoc.delete() + } + } } diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt new file mode 100644 index 000000000..c8eb919d2 --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt @@ -0,0 +1,663 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.runTest +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.time.Duration.Companion.milliseconds + +@IgnoreForAndroidUnitTest +open class QueryTest : BaseFirebaseFirestoreTest() { + + companion object { + const val COLLECTION = "testFirestoreQuerying" + val testOne = FirestoreTest( + "aaa", + 0.0, + 1, + listOf("a", "aa", "aaa"), + "notNull", + NestedObject("ddd"), + listOf(NestedObject("l1"), NestedObject("l2"), NestedObject("l3")), + 100.milliseconds, + ) + val testTwo = FirestoreTest( + "bbb", + 0.0, + 2, + listOf("b", "bb", "ccc"), + null, + NestedObject("eee"), + listOf(NestedObject("l2"), NestedObject("l4"), NestedObject("l5")), + 200.milliseconds, + ) + val testThree = FirestoreTest( + "ccc", + 1.0, + 3, + listOf("c", "cc", "ccc"), + "notNull", + NestedObject("fff"), + listOf(NestedObject("l3"), NestedObject("l6"), NestedObject("l7")), + 300.milliseconds, + ) + } + + private val collection get() = firestore.collection(COLLECTION) + + @Test + fun testStringOrderBy() = runTestWithFirestoreData { + val resultDocs = collection + .orderBy(FirestoreTest::prop1.name) + .get() + .documents + assertEquals(3, resultDocs.size) + assertEquals("aaa", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("ccc", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testFieldOrderBy() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FieldPath(FirestoreTest::prop1.name)).get().documents + assertEquals(3, resultDocs.size) + assertEquals("aaa", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("ccc", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testStringOrderByAscending() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING).get().documents + assertEquals(3, resultDocs.size) + assertEquals("aaa", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("ccc", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testFieldOrderByAscending() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FieldPath(FirestoreTest::prop1.name), Direction.ASCENDING).get().documents + assertEquals(3, resultDocs.size) + assertEquals("aaa", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("ccc", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testStringOrderByDescending() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FirestoreTest::prop1.name, Direction.DESCENDING).get().documents + assertEquals(3, resultDocs.size) + assertEquals("ccc", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("aaa", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testFieldOrderByDescending() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FieldPath(FirestoreTest::prop1.name), Direction.DESCENDING).get().documents + assertEquals(3, resultDocs.size) + assertEquals("ccc", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("aaa", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testQueryEqualTo() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FirestoreTest::prop1.name equalTo testOne.prop1 } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::prop1.name) equalTo testTwo.prop1 } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo) + + val nullableQuery = collection + .where { FieldPath(FirestoreTest::optional.name).isNull } + + nullableQuery.assertDocuments(FirestoreTest.serializer(), testTwo) + + val serializeFieldQuery = collection.where { + FirestoreTest::nested.name.equalTo(NestedObject.serializer(), testOne.nested!!) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nested.name).equalTo(NestedObject.serializer(), testTwo.nested!!) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testTwo) + } + + @Test + fun testQueryNotEqualTo() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FirestoreTest::prop1.name notEqualTo testOne.prop1 } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::prop1.name) notEqualTo testTwo.prop1 } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) + + val nullableQuery = collection + .where { FieldPath(FirestoreTest::optional.name).isNotNull } + + nullableQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) + + val serializeFieldQuery = collection.where { + FirestoreTest::nested.name.notEqualTo(NestedObject.serializer(), testOne.nested!!) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nested.name).notEqualTo(NestedObject.serializer(), testTwo.nested!!) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) + } + + @Test + fun testQueryLessThan() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "count" lessThan testThree.count } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::count.name) lessThan testTwo.count } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val serializeFieldQuery = collection.where { + "duration".lessThan(DurationAsIntSerializer(), testThree.duration) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::duration.name).lessThan(DurationAsIntSerializer(), testTwo.duration) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testQueryGreaterThan() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "count" greaterThan testOne.count } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::count.name) greaterThan testTwo.count } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val serializeFieldQuery = collection.where { + "duration".greaterThan(DurationAsIntSerializer(), testOne.duration) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::duration.name).greaterThan(DurationAsIntSerializer(), testTwo.duration) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + } + + @Test + fun testQueryLessThanOrEqualTo() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "count" lessThanOrEqualTo testOne.count } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::count.name) lessThanOrEqualTo testTwo.count } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializeFieldQuery = collection.where { + "duration".lessThanOrEqualTo(DurationAsIntSerializer(), testOne.duration) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::duration.name).lessThanOrEqualTo(DurationAsIntSerializer(), testTwo.duration) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + } + + @Test + fun testQueryGreaterThanOrEqualTo() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "count" greaterThanOrEqualTo testThree.count } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::count.name) greaterThanOrEqualTo testTwo.count } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val serializeFieldQuery = collection.where { + "duration".greaterThanOrEqualTo(DurationAsIntSerializer(), testTwo.duration) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::duration.name).greaterThanOrEqualTo(DurationAsIntSerializer(), testThree.duration) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + } + + @Test + fun testQueryArrayContains() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "list" contains "a" } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::list.name) contains "ccc" } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testThree, testTwo) + + val serializeFieldQuery = collection.where { + "nestedList".contains(NestedObject.serializer(), NestedObject("l2")) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nestedList.name).contains(NestedObject.serializer(), NestedObject("l3")) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) + } + + @Test + fun testQueryArrayContainsAny() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "list" containsAny listOf("a", "b") } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::list.name) containsAny listOf("c", "d") } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val serializeFieldQuery = collection.where { + "nestedList".containsAny(NestedObject.serializer(), listOf(NestedObject("l1"), NestedObject("l4"))) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nestedList.name).containsAny(NestedObject.serializer(), listOf(NestedObject("l5"), NestedObject("l7"))) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree, testTwo) + } + + @Test + fun testQueryInArray() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FirestoreTest::prop1.name inArray listOf("aaa", "bbb") } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::prop1.name) inArray listOf("ccc", "ddd") } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val serializeFieldQuery = collection.where { + FirestoreTest::nested.name.inArray(NestedObject.serializer(), listOf(NestedObject("ddd"), NestedObject("eee"))) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nested.name).inArray(NestedObject.serializer(), listOf(NestedObject("eee"), NestedObject("fff"))) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree, testTwo) + } + + @Test + fun testQueryNotInArray() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FirestoreTest::prop1.name notInArray listOf("aaa", "bbb") } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::prop1.name) notInArray listOf("ccc", "ddd") } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializeFieldQuery = collection.where { + FirestoreTest::nested.name.notInArray(NestedObject.serializer(), listOf(NestedObject("ddd"), NestedObject("eee"))) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nested.name).notInArray(NestedObject.serializer(), listOf(NestedObject("eee"), NestedObject("fff"))) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testCompoundQuery() = runTestWithFirestoreData { + val andQuery = collection + .where { + FieldPath(FirestoreTest::prop1.name) inArray listOf("aaa", "bbb") and (FieldPath(FirestoreTest::count.name) equalTo 1) + } + andQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val orQuery = collection + .where { + FieldPath(FirestoreTest::prop1.name) equalTo "aaa" or (FieldPath(FirestoreTest::count.name) equalTo 2) + } + orQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val andOrQuery = collection + .where { + all( + any( + FieldPath(FirestoreTest::prop1.name) equalTo "aaa", + FieldPath(FirestoreTest::count.name) equalTo 2, + )!!, + FieldPath(FirestoreTest::list.name) contains "a", + ) + } + andOrQuery.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testQueryByDocumentId() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FieldPath.documentId equalTo "one" } + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testQueryByTimestamp() = runTest { + @Serializable + data class DocumentWithTimestamp( + val time: Timestamp, + ) + + val collection = firestore + .collection("testQueryByTimestamp") + + val timestamp = Timestamp.fromMilliseconds(1693262549000.0) + + val pastTimestamp = Timestamp(timestamp.seconds - 60, 12345000) // note: iOS truncates 3 last digits of nanoseconds due to internal conversions + val futureTimestamp = Timestamp(timestamp.seconds + 60, 78910000) + + val doc1 = collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(pastTimestamp)) + val doc2 = collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(futureTimestamp)) + + try { + val equalityQueryResult = collection.where { + FieldPath(DocumentWithTimestamp::time.name) equalTo pastTimestamp + }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet() + + assertEquals(setOf(DocumentWithTimestamp(pastTimestamp)), equalityQueryResult) + + val gtQueryResult = collection.where { + FieldPath(DocumentWithTimestamp::time.name) greaterThan timestamp + }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet() + + assertEquals(setOf(DocumentWithTimestamp(futureTimestamp)), gtQueryResult) + } finally { + doc1.delete() + doc2.delete() + } + } + + @Test + fun testStartAfterDocumentSnapshot() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val firstPage = query.limit(2) + + firstPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + val lastDocumentSnapshot = firstPage.get().documents.lastOrNull() + assertNotNull(lastDocumentSnapshot) + + val secondPage = query.startAfter(lastDocumentSnapshot) + secondPage.assertDocuments(FirestoreTest.serializer(), testThree) + } + + @Test + fun testStartAfterFieldValues() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + query.assertDocuments(FirestoreTest.serializer(), testOne, testTwo, testThree) + + val secondPage = query.startAfter("bbb") + secondPage.assertDocuments(FirestoreTest.serializer(), testThree) + + val encodedQuery = collection + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedSecondPage = encodedQuery.startAfterFieldValues { + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedSecondPage.assertDocuments(FirestoreTest.serializer(), testThree) + + val multipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val multipleSecondPage = multipleQuery.startAfter(0.0, "aaa") + multipleSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val encodedMultipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedMultipleSecondPage = encodedMultipleQuery.startAfterFieldValues { + add(0.0) + addWithStrategy(NestedObject.serializer(), NestedObject("ddd")) + } + encodedMultipleSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + } + + @Test + fun testStartAtDocumentSnapshot() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val firstPage = query.limit(2) + firstPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val lastDocumentSnapshot = firstPage.get().documents.lastOrNull() + assertNotNull(lastDocumentSnapshot) + + val secondPage = query.startAt(lastDocumentSnapshot) + secondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + } + + @Test + fun testStartAtFieldValues() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val secondPage = query.startAt("bbb") + secondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val encodedQuery = collection + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedSecondPage = encodedQuery.startAtFieldValues { + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val multipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val multipleSecondPage = multipleQuery.startAt(0.0, "bbb") + multipleSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val encodedMultipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedMultipleSecondPage = encodedMultipleQuery.startAtFieldValues { + add(0.0) + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedMultipleSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + } + + @Test + fun testEndBeforeDocumentSnapshot() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val firstPage = query.limit(2) + firstPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val lastDocumentSnapshot = firstPage.get().documents.lastOrNull() + assertNotNull(lastDocumentSnapshot) + + val secondPage = query.endBefore(lastDocumentSnapshot) + secondPage.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testEndBeforeFieldValues() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val secondPage = query.endBefore("bbb") + secondPage.assertDocuments(FirestoreTest.serializer(), testOne) + + val encodedQuery = collection + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedSecondPage = encodedQuery.endBeforeFieldValues { + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + + val multipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val multipleSecondPage = multipleQuery.endBefore(0.0, "bbb") + multipleSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + + val encodedMultipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedMultipleSecondPage = encodedMultipleQuery.endBeforeFieldValues { + add(0.0) + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedMultipleSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testEndAtDocumentSnapshot() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val firstPage = query.limit(2) // First 2 results + firstPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val lastDocumentSnapshot = firstPage.get().documents.lastOrNull() + assertNotNull(lastDocumentSnapshot) + + val secondPage = query.endAt(lastDocumentSnapshot) + secondPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + } + + @Test + fun testEndAtFieldValues() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val secondPage = query.endAt("bbb") + secondPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val encodedQuery = collection + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedSecondPage = encodedQuery.endAtFieldValues { + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedSecondPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val multipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val multipleSecondPage = multipleQuery.endAt(0.0, "aaa") + multipleSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + + val encodedMultipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedMultipleSecondPage = encodedMultipleQuery.endAtFieldValues { + add(0.0) + addWithStrategy(NestedObject.serializer(), NestedObject("ddd")) + } + encodedMultipleSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + } + + private fun runTestWithFirestoreData( + documentOne: FirestoreTest = testOne, + documentTwo: FirestoreTest = testTwo, + documentThree: FirestoreTest = testThree, + block: suspend () -> Unit, + ) = runTest { + try { + setupFirestoreData(documentOne, documentTwo, documentThree) + block() + } finally { + cleanFirestoreData() + } + } + + private suspend fun setupFirestoreData( + documentOne: FirestoreTest = testOne, + documentTwo: FirestoreTest = testTwo, + documentThree: FirestoreTest = testThree, + ) { + firestore.collection(COLLECTION) + .document("one") + .set(FirestoreTest.serializer(), documentOne) + firestore.collection(COLLECTION) + .document("two") + .set(FirestoreTest.serializer(), documentTwo) + firestore.collection(COLLECTION) + .document("three") + .set(FirestoreTest.serializer(), documentThree) + } + + private suspend fun cleanFirestoreData() { + firestore.collection(COLLECTION).document("one").delete() + firestore.collection(COLLECTION).document("two").delete() + firestore.collection(COLLECTION).document("three").delete() + } + + private suspend fun Query.assertDocuments(serializer: KSerializer, vararg expected: T) { + val documents = get().documents + assertEquals(expected.size, documents.size) + documents.forEachIndexed { index, documentSnapshot -> + assertEquals(expected[index], documentSnapshot.data(serializer)) + } + } +} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt new file mode 100644 index 000000000..03684e3d6 --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt @@ -0,0 +1,53 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.milliseconds + +@IgnoreForAndroidUnitTest +class TransactionTest : BaseFirebaseFirestoreTest() { + + @Test + fun runTransaction() = runTest { + val collection = firestore.collection("testServerTestTransaction") + val document = collection.document("doc1") + try { + document.set( + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1", + count = 0, + ), + ) + val result = firestore.runTransaction { + val count = get(document).data(FirestoreTest.serializer()).count + + if (count < 1) { + updateFields(document) { + FirestoreTest::prop1.name to "newProperty" + FieldPath(FirestoreTest::count.name) to 5 + FirestoreTest::duration.name.to(DurationAsIntSerializer(), 100.milliseconds) + FieldPath(FirestoreTest::nested.name).to( + NestedObject.serializer(), + NestedObject("nested"), + ) + } + true + } else { + throw IllegalStateException("Invalid count") + } + } + assertTrue(result) + + val updated = document.get().data(FirestoreTest.serializer()) + assertEquals("newProperty", updated.prop1) + assertEquals(5, updated.count) + assertEquals(100.milliseconds, updated.duration) + assertEquals(NestedObject("nested"), updated.nested) + } finally { + document.delete() + } + } +} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt new file mode 100644 index 000000000..4eecdfbf9 --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt @@ -0,0 +1,210 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.milliseconds + +@IgnoreForAndroidUnitTest +class WriteBatchTest : BaseFirebaseFirestoreTest() { + + private val collection get() = firestore + .collection("testServerTestSetBatch") + + @Test + fun testSetBatch() = testBatch { doc1, doc2 -> + val batch = firestore.batch() + batch.set( + documentRef = doc1, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1", + time = 123.0, + ), + ) + batch.set( + documentRef = doc2, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop2", + time = 456.0, + ), + ) + batch.commit() + + assertEquals("prop1", doc1.get().data(FirestoreTest.serializer()).prop1) + assertEquals("prop2", doc2.get().data(FirestoreTest.serializer()).prop1) + } + + @Test + fun testSetBatchDoesNotEncodeEmptyValues() = testBatch { doc1, doc2 -> + val batch = firestore.batch() + batch.set( + documentRef = doc1, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1-set", + time = 125.0, + ), + ) + batch.set( + documentRef = doc2, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop2-set", + time = 250.0, + ), + ) + batch.commit() + + assertEquals(125.0, doc1.get().get("time") as Double?) + assertEquals("prop1-set", doc1.get().data(FirestoreTest.serializer()).prop1) + assertEquals(250.0, doc2.get().get("time") as Double?) + assertEquals("prop2-set", doc2.get().data(FirestoreTest.serializer()).prop1) + } + + @Test + fun testUpdateBatch() = testBatch { doc1, doc2 -> + doc1.set( + FirestoreTest( + prop1 = "prop1", + time = 123.0, + ), + ) + doc2.set( + FirestoreTest( + prop1 = "prop2", + time = 456.0, + ), + ) + + val batch = firestore.batch() + batch.update( + documentRef = doc1, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1-updated", + time = 345.0, + ), + ) { + encodeDefaults = false + } + batch.update( + documentRef = doc2, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop2-updated", + time = 567.0, + ), + ) { + encodeDefaults = false + } + batch.commit() + + assertEquals("prop1-updated", doc1.get().data(FirestoreTest.serializer()).prop1) + assertEquals("prop2-updated", doc2.get().data(FirestoreTest.serializer()).prop1) + } + + @Test + fun testUpdateBatchDoesNotEncodeEmptyValues() = testBatch { doc1, doc2 -> + doc1.set( + FirestoreTest( + prop1 = "prop1", + time = 123.0, + ), + ) + doc2.set( + FirestoreTest( + prop1 = "prop2", + time = 456.0, + ), + ) + val batch = firestore.batch() + batch.update( + documentRef = doc1, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1-set", + time = 126.0, + ), + ) { + encodeDefaults = false + } + batch.update( + documentRef = doc2, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop2-set", + time = 457.0, + ), + ) { + encodeDefaults = false + } + batch.commit() + + assertEquals(126.0, doc1.get().get("time") as Double?) + assertEquals("prop1-set", doc1.get().data(FirestoreTest.serializer()).prop1) + assertEquals(457.0, doc2.get().get("time") as Double?) + assertEquals("prop2-set", doc2.get().data(FirestoreTest.serializer()).prop1) + } + + @Test + fun testUpdateFieldValuesBatch() = testBatch { doc1, doc2 -> + doc1.set( + FirestoreTest( + prop1 = "prop1", + time = 123.0, + duration = 800.milliseconds, + ), + ) + + doc2.set( + FirestoreTest( + prop1 = "prop2", + time = 456.0, + duration = 700.milliseconds, + ), + ) + + val batch = firestore.batch() + batch.updateFields(doc1) { + FirestoreTest::prop1.name to "prop1-updated" + FieldPath(FirestoreTest::optional.name) to "notNull" + FirestoreTest::duration.name.to(DurationAsIntSerializer(), 300.milliseconds) + FieldPath(FirestoreTest::nested.name).to(NestedObject.serializer(), NestedObject("nested")) + } + batch.updateFields(doc2) { + FirestoreTest::prop1.name to "prop2-updated" + FieldPath(FirestoreTest::optional.name) to "alsoNotNull" + FirestoreTest::duration.name.to(DurationAsIntSerializer(), 200.milliseconds) + FieldPath(FirestoreTest::nested.name).to(NestedObject.serializer(), NestedObject("alsoNested")) + } + batch.commit() + + val updatedDoc1 = doc1.get().data(FirestoreTest.serializer()) + assertEquals("prop1-updated", updatedDoc1.prop1) + assertEquals("notNull", updatedDoc1.optional) + assertEquals(300.milliseconds, updatedDoc1.duration) + assertEquals(NestedObject("nested"), updatedDoc1.nested) + + val updatedDoc2 = doc2.get().data(FirestoreTest.serializer()) + assertEquals("prop2-updated", updatedDoc2.prop1) + assertEquals("alsoNotNull", updatedDoc2.optional) + assertEquals(200.milliseconds, updatedDoc2.duration) + assertEquals(NestedObject("alsoNested"), updatedDoc2.nested) + } + + private fun testBatch(block: suspend (DocumentReference, DocumentReference) -> Unit) = runTest { + val doc1 = collection + .document("test1") + val doc2 = collection + .document("test2") + + try { + block(doc1, doc2) + } finally { + doc1.delete() + doc2.delete() + } + } +} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 99148d8cc..7f79a7ebc 100644 --- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -8,31 +8,19 @@ import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.FirebaseApp import dev.gitlive.firebase.FirebaseOptions import dev.gitlive.firebase.apps -import dev.gitlive.firebase.internal.decode import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runBlockingTest import dev.gitlive.firebase.runTest -import dev.gitlive.firebase.internal.withSerializer -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.withContext import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.builtins.ListSerializer -import kotlinx.serialization.builtins.nullable -import kotlinx.serialization.builtins.serializer -import kotlin.random.Random +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -46,7 +34,7 @@ expect fun encodedAsMap(encoded: Any?): Map expect fun Map.asEncoded(): Any @IgnoreForAndroidUnitTest -class FirebaseFirestoreTest { +abstract class BaseFirebaseFirestoreTest { @Serializable data class FirestoreTest( @@ -55,35 +43,27 @@ class FirebaseFirestoreTest { val count: Int = 0, val list: List = emptyList(), val optional: String? = null, + val nested: NestedObject? = null, + val nestedList: List = emptyList(), + @Serializable(with = DurationAsIntSerializer::class) + val duration: Duration = Duration.ZERO, ) @Serializable - data class FirestoreTimeTest( - val prop1: String, - val time: BaseTimestamp?, + data class NestedObject( + val prop2: String, ) - companion object { - val testOne = FirestoreTest( - "aaa", - 0.0, - 1, - listOf("a", "aa", "aaa"), - "notNull", - ) - val testTwo = FirestoreTest( - "bbb", - 0.0, - 2, - listOf("b", "bb", "ccc"), - ) - val testThree = FirestoreTest( - "ccc", - 1.0, - 3, - listOf("c", "cc", "ccc"), - "notNull", - ) + // Long would be better but JS does not seem to support it on the Firebase level https://stackoverflow.com/questions/31930406/storing-long-type-in-firebase + class DurationAsIntSerializer : KSerializer { + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("millisecondsSinceEpoch", PrimitiveKind.INT) + + override fun serialize(encoder: Encoder, value: Duration) { + encoder.encodeInt(value.inWholeMilliseconds.toInt()) + } + + override fun deserialize(decoder: Decoder): Duration = decoder.decodeInt().milliseconds } lateinit var firebaseApp: FirebaseApp @@ -120,951 +100,14 @@ class FirebaseFirestoreTest { it.delete() } } +} - @Test - fun testStringOrderBy() = runTest { - setupFirestoreData() - val resultDocs = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1") - .get() - .documents - assertEquals(3, resultDocs.size) - assertEquals("aaa", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("ccc", resultDocs[2].get("prop1")) - } - - @Test - fun testFieldOrderBy() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy(FieldPath("prop1")).get().documents - assertEquals(3, resultDocs.size) - assertEquals("aaa", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("ccc", resultDocs[2].get("prop1")) - } - - @Test - fun testStringOrderByAscending() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING).get().documents - assertEquals(3, resultDocs.size) - assertEquals("aaa", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("ccc", resultDocs[2].get("prop1")) - } - - @Test - fun testFieldOrderByAscending() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy(FieldPath("prop1"), Direction.ASCENDING).get().documents - assertEquals(3, resultDocs.size) - assertEquals("aaa", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("ccc", resultDocs[2].get("prop1")) - } - - @Test - fun testStringOrderByDescending() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy("prop1", Direction.DESCENDING).get().documents - assertEquals(3, resultDocs.size) - assertEquals("ccc", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("aaa", resultDocs[2].get("prop1")) - } - - @Test - fun testFieldOrderByDescending() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy(FieldPath("prop1"), Direction.DESCENDING).get().documents - assertEquals(3, resultDocs.size) - assertEquals("ccc", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("aaa", resultDocs[2].get("prop1")) - } - - @Test - fun testServerTimestampFieldValue() = runTest { - val doc = firestore - .collection("testServerTimestampFieldValue") - .document("test") - doc.set( - FirestoreTimeTest.serializer(), - FirestoreTimeTest("ServerTimestamp", Timestamp(123, 0)), - ) - assertEquals(Timestamp(123, 0), doc.get().get("time", TimestampSerializer)) - - doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestamp", Timestamp.ServerTimestamp)) - - assertNotEquals(Timestamp.ServerTimestamp, doc.get().get("time", BaseTimestamp.serializer())) - assertNotEquals(Timestamp.ServerTimestamp, doc.get().data(FirestoreTimeTest.serializer()).time) - } - - @Test - fun testServerTimestampBehaviorNone() = runTest { - val doc = firestore - .collection("testServerTimestampBehaviorNone") - .document("test${Random.nextInt()}") - - val deferredPendingWritesSnapshot = async { - doc.snapshots.filter { it.exists }.first() - } - nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot - - doc.set( - FirestoreTimeTest.serializer(), - FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp), - ) - - val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() - assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) - assertNull(pendingWritesSnapshot.get("time", BaseTimestamp.serializer().nullable, serverTimestampBehavior = ServerTimestampBehavior.NONE)) - } - - @Test - fun testSetBatch() = runTest { - val doc = firestore - .collection("testServerTestSetBatch") - .document("test") - val batch = firestore.batch() - batch.set( - documentRef = doc, - strategy = FirestoreTest.serializer(), - data = FirestoreTest( - prop1 = "prop1", - time = 123.0, - ), - ) - batch.commit() - - assertEquals("prop1", doc.get().data(FirestoreTest.serializer()).prop1) - } - - @Test - fun testServerTimestampBehaviorEstimate() = runTest { - val doc = firestore - .collection("testServerTimestampBehaviorEstimate") - .document("test${Random.nextInt()}") - - val deferredPendingWritesSnapshot = async { - doc.snapshots.filter { it.exists }.first() - } - nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot - - doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp)) - - val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() - assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) - assertNotNull(pendingWritesSnapshot.get("time", ServerTimestampBehavior.ESTIMATE)) - assertNotEquals(Timestamp.ServerTimestamp, pendingWritesSnapshot.data(FirestoreTimeTest.serializer(), serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE).time) - } - - @Test - fun testServerTimestampBehaviorPrevious() = runTest { - val doc = firestore - .collection("testServerTimestampBehaviorPrevious") - .document("test${Random.nextInt()}") - - val deferredPendingWritesSnapshot = async { - doc.snapshots.filter { it.exists }.first() - } - nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot - - doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp)) - - val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() - assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) - assertNull(pendingWritesSnapshot.get("time", BaseTimestamp.serializer().nullable, serverTimestampBehavior = ServerTimestampBehavior.PREVIOUS)) - } - - @Test - fun testDocumentAutoId() = runTest { - val doc = firestore - .collection("testDocumentAutoId") - .document - - doc.set(FirestoreTest.serializer(), FirestoreTest("AutoId")) - - val resultDoc = firestore - .collection("testDocumentAutoId") - .document(doc.id) - .get() - - assertEquals(true, resultDoc.exists) - assertEquals("AutoId", resultDoc.get("prop1")) - } - - @Test - fun testStartAfterDocumentSnapshot() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.limit(2).get().documents // First 2 results - assertEquals(2, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - - val lastDocumentSnapshot = firstPage.lastOrNull() - assertNotNull(lastDocumentSnapshot) - - val secondPage = query.startAfter(lastDocumentSnapshot).get().documents - assertEquals(1, secondPage.size) - assertEquals("ccc", secondPage[0].get("prop1")) - } - - @Test - fun testStartAfterFieldValues() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.get().documents - assertEquals(3, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - assertEquals("ccc", firstPage[2].get("prop1")) - - val secondPage = query.startAfter("bbb").get().documents - assertEquals(1, secondPage.size) - assertEquals("ccc", secondPage[0].get("prop1")) - } - - @Test - fun testStartAtDocumentSnapshot() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.limit(2).get().documents // First 2 results - assertEquals(2, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - - val lastDocumentSnapshot = firstPage.lastOrNull() - assertNotNull(lastDocumentSnapshot) - - val secondPage = query.startAt(lastDocumentSnapshot).get().documents - assertEquals(2, secondPage.size) - assertEquals("bbb", secondPage[0].get("prop1")) - assertEquals("ccc", secondPage[1].get("prop1")) - } - - @Test - fun testStartAtFieldValues() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.get().documents // First 2 results - assertEquals(3, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - assertEquals("ccc", firstPage[2].get("prop1")) - - val secondPage = query.startAt("bbb").get().documents - assertEquals(2, secondPage.size) - assertEquals("bbb", secondPage[0].get("prop1")) - assertEquals("ccc", secondPage[1].get("prop1")) - } - - @Test - fun testEndBeforeDocumentSnapshot() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.limit(2).get().documents // First 2 results - assertEquals(2, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - - val lastDocumentSnapshot = firstPage.lastOrNull() - assertNotNull(lastDocumentSnapshot) - - val secondPage = query.endBefore(lastDocumentSnapshot).get().documents - assertEquals(1, secondPage.size) - assertEquals("aaa", secondPage[0].get("prop1")) - } - - @Test - fun testEndBeforeFieldValues() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.get().documents - assertEquals(3, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - assertEquals("ccc", firstPage[2].get("prop1")) - - val secondPage = query.endBefore("bbb").get().documents - assertEquals(1, secondPage.size) - assertEquals("aaa", secondPage[0].get("prop1")) - } - - @Test - fun testEndAtDocumentSnapshot() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.limit(2).get().documents // First 2 results - assertEquals(2, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - - val lastDocumentSnapshot = firstPage.lastOrNull() - assertNotNull(lastDocumentSnapshot) - - val secondPage = query.endAt(lastDocumentSnapshot).get().documents - assertEquals(2, secondPage.size) - assertEquals("aaa", secondPage[0].get("prop1")) - assertEquals("bbb", secondPage[1].get("prop1")) - } - - @Test - fun testEndAtFieldValues() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.get().documents // First 2 results - assertEquals(3, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - assertEquals("ccc", firstPage[2].get("prop1")) - - val secondPage = query.endAt("bbb").get().documents - assertEquals(2, secondPage.size) - assertEquals("aaa", secondPage[0].get("prop1")) - assertEquals("bbb", secondPage[1].get("prop1")) - } - - @Test - fun testIncrementFieldValue() = runTest { - val doc = firestore - .collection("testFirestoreIncrementFieldValue") - .document("test1") - - doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", count = 0)) - val dataBefore = doc.get().data(FirestoreTest.serializer()) - assertEquals(0, dataBefore.count) - - doc.update("count" to FieldValue.increment(5)) - val dataAfter = doc.get().data(FirestoreTest.serializer()) - assertEquals(5, dataAfter.count) - } - - @Test - fun testArrayUnion() = runTest { - val doc = firestore - .collection("testFirestoreArrayUnion") - .document("test1") - - doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first"))) - val dataBefore = doc.get().data(FirestoreTest.serializer()) - assertEquals(listOf("first"), dataBefore.list) - - doc.update("list" to FieldValue.arrayUnion("second")) - val dataAfter = doc.get().data(FirestoreTest.serializer()) - assertEquals(listOf("first", "second"), dataAfter.list) - } - - @Test - fun testArrayRemove() = runTest { - val doc = firestore - .collection("testFirestoreArrayRemove") - .document("test1") - - doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first", "second"))) - val dataBefore = doc.get().data(FirestoreTest.serializer()) - assertEquals(listOf("first", "second"), dataBefore.list) - - doc.update("list" to FieldValue.arrayRemove("second")) - val dataAfter = doc.get().data(FirestoreTest.serializer()) - assertEquals(listOf("first"), dataAfter.list) - } - - @Test - fun testSetBatchDoesNotEncodeEmptyValues() = runTest { - val doc = firestore - .collection("testServerTestSetBatch") - .document("test") - val batch = firestore.batch() - batch.set( - documentRef = doc, - strategy = FirestoreTest.serializer(), - data = FirestoreTest( - prop1 = "prop1-set", - time = 125.0, - ), - ) - batch.commit() - - assertEquals(125.0, doc.get().get("time") as Double?) - assertEquals("prop1-set", doc.get().data(FirestoreTest.serializer()).prop1) - } - - @Test - fun testUpdateBatch() = runTest { - val doc = firestore - .collection("testServerTestSetBatch") - .document("test").apply { - set( - FirestoreTest( - prop1 = "prop1", - time = 123.0, - ), - ) - } - - val batch = firestore.batch() - batch.update( - documentRef = doc, - strategy = FirestoreTest.serializer(), - data = FirestoreTest( - prop1 = "prop1-updated", - time = 123.0, - ), - ) { - encodeDefaults = false - } - batch.commit() - - assertEquals("prop1-updated", doc.get().data(FirestoreTest.serializer()).prop1) - } - - @Test - fun testUpdateBatchDoesNotEncodeEmptyValues() = runTest { - val doc = firestore - .collection("testServerTestSetBatch") - .document("test").apply { - set( - FirestoreTest( - prop1 = "prop1", - time = 123.0, - ), - ) - } - val batch = firestore.batch() - batch.update( - documentRef = doc, - strategy = FirestoreTest.serializer(), - data = FirestoreTest( - prop1 = "prop1-set", - time = 126.0, - ), - ) { - encodeDefaults = false - } - batch.commit() - - assertEquals(126.0, doc.get().get("time") as Double?) - assertEquals("prop1-set", doc.get().data(FirestoreTest.serializer()).prop1) - } - - @Test - fun testLegacyDoubleTimestamp() = runTest { - @Serializable - data class DoubleTimestamp( - @Serializable(with = DoubleAsTimestampSerializer::class) - val time: Double?, - ) - - val doc = firestore - .collection("testLegacyDoubleTimestamp") - .document("test${Random.nextInt()}") - - val deferredPendingWritesSnapshot = async { - doc.snapshots.filter { it.exists }.first() - } - nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot - - doc.set(DoubleTimestamp.serializer(), DoubleTimestamp(DoubleAsTimestampSerializer.SERVER_TIMESTAMP)) - - val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() - assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) - assertNotNull(pendingWritesSnapshot.get("time", DoubleAsTimestampSerializer, serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE)) - assertNotEquals(DoubleAsTimestampSerializer.SERVER_TIMESTAMP, pendingWritesSnapshot.data(DoubleTimestamp.serializer(), serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE).time) - } - - @Test - fun testLegacyDoubleTimestampWriteNewFormatRead() = runTest { - @Serializable - data class LegacyDocument( - @Serializable(with = DoubleAsTimestampSerializer::class) - val time: Double, - ) - - @Serializable - data class NewDocument( - val time: Timestamp, - ) - - val doc = firestore - .collection("testLegacyDoubleTimestampEncodeDecode") - .document("testLegacy") - - val ms = 12345678.0 - - doc.set(LegacyDocument.serializer(), LegacyDocument(time = ms)) - - val fetched: NewDocument = doc.get().data(NewDocument.serializer()) - assertEquals(ms, fetched.time.toMilliseconds()) - } - - @Test - fun testQueryByTimestamp() = runTest { - @Serializable - data class DocumentWithTimestamp( - val time: Timestamp, - ) - - val collection = firestore - .collection("testQueryByTimestamp") - - val timestamp = Timestamp.fromMilliseconds(1693262549000.0) - - val pastTimestamp = Timestamp(timestamp.seconds - 60, 12345000) // note: iOS truncates 3 last digits of nanoseconds due to internal conversions - val futureTimestamp = Timestamp(timestamp.seconds + 60, 78910000) - - collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(pastTimestamp)) - collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(futureTimestamp)) - - val equalityQueryResult = collection.where { - FieldPath(DocumentWithTimestamp::time.name) equalTo pastTimestamp - }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet() - - assertEquals(setOf(DocumentWithTimestamp(pastTimestamp)), equalityQueryResult) - - val gtQueryResult = collection.where { - FieldPath(DocumentWithTimestamp::time.name) greaterThan timestamp - }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet() - - assertEquals(setOf(DocumentWithTimestamp(futureTimestamp)), gtQueryResult) - } - - @Test - fun testGeoPointSerialization() = runTest { - @Serializable - data class DataWithGeoPoint(val geoPoint: GeoPoint) - - fun getDocument() = firestore.collection("geoPointSerialization") - .document("geoPointSerialization") - - val data = DataWithGeoPoint(GeoPoint(12.34, 56.78)) - // store geo point - getDocument().set(DataWithGeoPoint.serializer(), data) - // restore data - val savedData = getDocument().get().data(DataWithGeoPoint.serializer()) - assertEquals(data.geoPoint, savedData.geoPoint) - - // update data - val updatedData = DataWithGeoPoint(GeoPoint(87.65, 43.21)) - getDocument().update(FieldPath(DataWithGeoPoint::geoPoint.name) to updatedData.geoPoint) - // verify update - val updatedSavedData = getDocument().get().data(DataWithGeoPoint.serializer()) - assertEquals(updatedData.geoPoint, updatedSavedData.geoPoint) - } - - @Test - fun testDocumentReferenceSerialization() = runTest { - @Serializable - data class DataWithDocumentReference( - val documentReference: DocumentReference, - ) - - fun getCollection() = firestore.collection("documentReferenceSerialization") - fun getDocument() = getCollection() - .document("documentReferenceSerialization") - val documentRef1 = getCollection().document("refDoc1").apply { - set(mapOf("value" to 1)) - } - val documentRef2 = getCollection().document("refDoc2").apply { - set(mapOf("value" to 2)) - } - - val data = DataWithDocumentReference(documentRef1) - // store reference - getDocument().set(DataWithDocumentReference.serializer(), data) - // restore data - val savedData = getDocument().get().data(DataWithDocumentReference.serializer()) - assertEquals(data.documentReference.path, savedData.documentReference.path) - - // update data - val updatedData = DataWithDocumentReference(documentRef2) - getDocument().update( - FieldPath(DataWithDocumentReference::documentReference.name) to updatedData.documentReference.withSerializer(DocumentReferenceSerializer), - ) - // verify update - val updatedSavedData = getDocument().get().data(DataWithDocumentReference.serializer()) - assertEquals(updatedData.documentReference.path, updatedSavedData.documentReference.path) - } - - @Serializable - data class TestDataWithDocumentReference( - val uid: String, - val reference: DocumentReference, - val optionalReference: DocumentReference?, - ) - - @Serializable - data class TestDataWithOptionalDocumentReference( - val optionalReference: DocumentReference?, - ) - - @Test - fun encodeDocumentReference() = runTest { - val doc = firestore.document("a/b") - val item = TestDataWithDocumentReference("123", doc, doc) - val encoded = encodedAsMap( - encode(item) { - encodeDefaults = false - }, - ) - assertEquals("123", encoded["uid"]) - assertEquals(doc.nativeValue, encoded["reference"]) - assertEquals(doc.nativeValue, encoded["optionalReference"]) - } - - @Test - fun encodeNullDocumentReference() = runTest { - val item = TestDataWithOptionalDocumentReference(null) - val encoded = encodedAsMap( - encode(item) { - encodeDefaults = false - }, - ) - assertNull(encoded["optionalReference"]) - } - - @Test - fun decodeDocumentReference() = runTest { - val doc = firestore.document("a/b") - val obj = mapOf( - "uid" to "123", - "reference" to doc.nativeValue, - "optionalReference" to doc.nativeValue, - ).asEncoded() - val decoded: TestDataWithDocumentReference = decode(obj) - assertEquals("123", decoded.uid) - assertEquals(doc.path, decoded.reference.path) - assertEquals(doc.path, decoded.optionalReference?.path) - } - - @Test - fun decodeNullDocumentReference() = runTest { - val obj = mapOf("optionalReference" to null).asEncoded() - val decoded: TestDataWithOptionalDocumentReference = decode(obj) - assertNull(decoded.optionalReference?.path) - } - - @Test - fun testFieldValuesOps() = runTest { - @Serializable - data class TestData(val values: List) - fun getDocument() = firestore.collection("fieldValuesOps") - .document("fieldValuesOps") - - val data = TestData(listOf(1)) - // store - getDocument().set(TestData.serializer(), data) - // append & verify - getDocument().update(FieldPath(TestData::values.name) to FieldValue.arrayUnion(2)) - - var savedData = getDocument().get().data(TestData.serializer()) - assertEquals(listOf(1, 2), savedData.values) - - // remove & verify - getDocument().update(FieldPath(TestData::values.name) to FieldValue.arrayRemove(1)) - savedData = getDocument().get().data(TestData.serializer()) - assertEquals(listOf(2), savedData.values) - - val list = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) - assertEquals(listOf(2), list) - // delete & verify - getDocument().update(FieldPath(TestData::values.name) to FieldValue.delete) - val deletedList = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) - assertNull(deletedList) - } - - @Test - fun testQueryEqualTo() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "prop1" equalTo testOne.prop1 } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::prop1.name) equalTo testTwo.prop1 } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo) - - val nullableQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::optional.name) equalTo null } - - nullableQuery.assertDocuments(FirestoreTest.serializer(), testTwo) - } - - @Test - fun testQueryNotEqualTo() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "prop1" notEqualTo testOne.prop1 } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::prop1.name) notEqualTo testTwo.prop1 } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) - - val nullableQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::optional.name) notEqualTo null } - - nullableQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) - } - - @Test - fun testQueryLessThan() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "count" lessThan testThree.count } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::count.name) lessThan testTwo.count } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testOne) - } - - @Test - fun testQueryGreaterThan() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "count" greaterThan testOne.count } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::count.name) greaterThan testTwo.count } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) - } - - @Test - fun testQueryLessThanOrEqualTo() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "count" lessThanOrEqualTo testOne.count } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::count.name) lessThanOrEqualTo testTwo.count } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - } - - @Test - fun testQueryGreaterThanOrEqualTo() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "count" greaterThanOrEqualTo testThree.count } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::count.name) greaterThanOrEqualTo testTwo.count } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) - } - - @Test - fun testQueryArrayContains() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "list" contains "a" } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::list.name) contains "ccc" } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testThree, testTwo) - } - - @Test - fun testQueryArrayContainsAny() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "list" containsAny listOf("a", "b") } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::list.name) containsAny listOf("c", "d") } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) - } - - @Test - fun testQueryInArray() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "prop1" inArray listOf("aaa", "bbb") } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::prop1.name) inArray listOf("ccc", "ddd") } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) - } - - @Test - fun testQueryNotInArray() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "prop1" notInArray listOf("aaa", "bbb") } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::prop1.name) notInArray listOf("ccc", "ddd") } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - } - - @Test - fun testCompoundQuery() = runTest { - setupFirestoreData() - - val andQuery = firestore - .collection("testFirestoreQuerying") - .where { - FieldPath(FirestoreTest::prop1.name) inArray listOf("aaa", "bbb") and (FieldPath(FirestoreTest::count.name) equalTo 1) - } - andQuery.assertDocuments(FirestoreTest.serializer(), testOne) - - val orQuery = firestore - .collection("testFirestoreQuerying") - .where { - FieldPath(FirestoreTest::prop1.name) equalTo "aaa" or (FieldPath(FirestoreTest::count.name) equalTo 2) - } - orQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - - val andOrQuery = firestore - .collection("testFirestoreQuerying") - .where { - all( - any( - FieldPath(FirestoreTest::prop1.name) equalTo "aaa", - FieldPath(FirestoreTest::count.name) equalTo 2, - )!!, - FieldPath(FirestoreTest::list.name) contains "a", - ) - } - andOrQuery.assertDocuments(FirestoreTest.serializer(), testOne) - } - - @Test - fun testQueryByDocumentId() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath.documentId equalTo "one" } - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) - } +@IgnoreForAndroidUnitTest +class FirebaseFirestoreTest : BaseFirebaseFirestoreTest() { @Test fun testMultiple() = runTest { Firebase.firestore(firebaseApp).disableNetwork() Firebase.firestore(firebaseApp).enableNetwork() } - - private suspend fun setupFirestoreData( - documentOne: FirestoreTest = testOne, - documentTwo: FirestoreTest = testTwo, - documentThree: FirestoreTest = testThree, - ) { - firestore.collection("testFirestoreQuerying") - .document("one") - .set(FirestoreTest.serializer(), documentOne) - firestore.collection("testFirestoreQuerying") - .document("two") - .set(FirestoreTest.serializer(), documentTwo) - firestore.collection("testFirestoreQuerying") - .document("three") - .set(FirestoreTest.serializer(), documentThree) - } - - private suspend fun Query.assertDocuments(serializer: KSerializer, vararg expected: T) { - val documents = get().documents - assertEquals(expected.size, documents.size) - documents.forEachIndexed { index, documentSnapshot -> - assertEquals(expected[index], documentSnapshot.data(serializer)) - } - } - - private suspend fun nonSkippedDelay(timeout: Duration) = withContext(Dispatchers.Default) { - delay(timeout) - } } diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index 98856556b..4f2c72d14 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -1,10 +1,10 @@ package dev.gitlive.firebase.firestore.internal -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.Source import dev.gitlive.firebase.firestore.await import dev.gitlive.firebase.firestore.awaitResult +import dev.gitlive.firebase.firestore.toEncodedMap import dev.gitlive.firebase.firestore.toException import dev.gitlive.firebase.internal.EncodedObject import dev.gitlive.firebase.internal.ios @@ -55,14 +55,9 @@ internal actual class NativeDocumentReference actual constructor(actual val nati ios.updateData(encodedData.ios, it) } - actual suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) = + actual suspend fun updateEncoded(encodedFieldsAndValues: List) = await { - ios.updateData(encodedFieldsAndValues.toMap(), it) - } - - actual suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) = - await { - ios.updateData(encodedFieldsAndValues.toMap(), it) + ios.updateData(encodedFieldsAndValues.toEncodedMap(), it) } actual suspend fun delete() = await { ios.deleteDocumentWithCompletion(it) } diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt index 30f01b7f1..f17bf14c8 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt @@ -45,28 +45,28 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is Filter.And -> FIRFilter.andFilterWithFilters(filters.map { it.toFIRFilter() }) is Filter.Or -> FIRFilter.orFilterWithFilters(filters.map { it.toFIRFilter() }) is Filter.Field -> when (constraint) { - is WhereConstraint.EqualTo -> FIRFilter.filterWhereField(field, isEqualTo = constraint.safeValue ?: NSNull.`null`()) - is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereField(field, isNotEqualTo = constraint.safeValue ?: NSNull.`null`()) - is WhereConstraint.LessThan -> FIRFilter.filterWhereField(field, isLessThan = constraint.safeValue) - is WhereConstraint.GreaterThan -> FIRFilter.filterWhereField(field, isGreaterThan = constraint.safeValue) - is WhereConstraint.LessThanOrEqualTo -> FIRFilter.filterWhereField(field, isLessThanOrEqualTo = constraint.safeValue) - is WhereConstraint.GreaterThanOrEqualTo -> FIRFilter.filterWhereField(field, isGreaterThanOrEqualTo = constraint.safeValue) - is WhereConstraint.ArrayContains -> FIRFilter.filterWhereField(field, arrayContains = constraint.safeValue) - is WhereConstraint.ArrayContainsAny -> FIRFilter.filterWhereField(field, arrayContainsAny = constraint.safeValues) - is WhereConstraint.InArray -> FIRFilter.filterWhereField(field, `in` = constraint.safeValues) - is WhereConstraint.NotInArray -> FIRFilter.filterWhereField(field, notIn = constraint.safeValues) + is WhereConstraint.EqualTo -> FIRFilter.filterWhereField(field, isEqualTo = constraint.value ?: NSNull.`null`()) + is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereField(field, isNotEqualTo = constraint.value ?: NSNull.`null`()) + is WhereConstraint.LessThan -> FIRFilter.filterWhereField(field, isLessThan = constraint.value) + is WhereConstraint.GreaterThan -> FIRFilter.filterWhereField(field, isGreaterThan = constraint.value) + is WhereConstraint.LessThanOrEqualTo -> FIRFilter.filterWhereField(field, isLessThanOrEqualTo = constraint.value) + is WhereConstraint.GreaterThanOrEqualTo -> FIRFilter.filterWhereField(field, isGreaterThanOrEqualTo = constraint.value) + is WhereConstraint.ArrayContains -> FIRFilter.filterWhereField(field, arrayContains = constraint.value) + is WhereConstraint.ArrayContainsAny -> FIRFilter.filterWhereField(field, arrayContainsAny = constraint.values) + is WhereConstraint.InArray -> FIRFilter.filterWhereField(field, `in` = constraint.values) + is WhereConstraint.NotInArray -> FIRFilter.filterWhereField(field, notIn = constraint.values) } is Filter.Path -> when (constraint) { - is WhereConstraint.EqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isEqualTo = constraint.safeValue ?: NSNull.`null`()) - is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isNotEqualTo = constraint.safeValue ?: NSNull.`null`()) - is WhereConstraint.LessThan -> FIRFilter.filterWhereFieldPath(path.ios, isLessThan = constraint.safeValue) - is WhereConstraint.GreaterThan -> FIRFilter.filterWhereFieldPath(path.ios, isGreaterThan = constraint.safeValue) - is WhereConstraint.LessThanOrEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isLessThanOrEqualTo = constraint.safeValue) - is WhereConstraint.GreaterThanOrEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isGreaterThanOrEqualTo = constraint.safeValue) - is WhereConstraint.ArrayContains -> FIRFilter.filterWhereFieldPath(path.ios, arrayContains = constraint.safeValue) - is WhereConstraint.ArrayContainsAny -> FIRFilter.filterWhereFieldPath(path.ios, arrayContainsAny = constraint.safeValues) - is WhereConstraint.InArray -> FIRFilter.filterWhereFieldPath(path.ios, `in` = constraint.safeValues) - is WhereConstraint.NotInArray -> FIRFilter.filterWhereFieldPath(path.ios, notIn = constraint.safeValues) + is WhereConstraint.EqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isEqualTo = constraint.value ?: NSNull.`null`()) + is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isNotEqualTo = constraint.value ?: NSNull.`null`()) + is WhereConstraint.LessThan -> FIRFilter.filterWhereFieldPath(path.ios, isLessThan = constraint.value) + is WhereConstraint.GreaterThan -> FIRFilter.filterWhereFieldPath(path.ios, isGreaterThan = constraint.value) + is WhereConstraint.LessThanOrEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isLessThanOrEqualTo = constraint.value) + is WhereConstraint.GreaterThanOrEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isGreaterThanOrEqualTo = constraint.value) + is WhereConstraint.ArrayContains -> FIRFilter.filterWhereFieldPath(path.ios, arrayContains = constraint.value) + is WhereConstraint.ArrayContainsAny -> FIRFilter.filterWhereFieldPath(path.ios, arrayContainsAny = constraint.values) + is WhereConstraint.InArray -> FIRFilter.filterWhereFieldPath(path.ios, `in` = constraint.values) + is WhereConstraint.NotInArray -> FIRFilter.filterWhereFieldPath(path.ios, notIn = constraint.values) } } diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt index a9fe0014d..4a95a6610 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt @@ -2,8 +2,8 @@ package dev.gitlive.firebase.firestore.internal import cocoapods.FirebaseFirestoreInternal.FIRTransaction import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.ios +import dev.gitlive.firebase.firestore.toEncodedMap import dev.gitlive.firebase.internal.EncodedObject import dev.gitlive.firebase.internal.ios @@ -22,19 +22,11 @@ internal actual class NativeTransactionWrapper actual constructor(actual val nat actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeTransactionWrapper = native.updateData(encodedData.ios, documentRef.ios).let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, + encodedFieldsAndValues: List, ): NativeTransactionWrapper = native.updateData( - encodedFieldsAndValues.toMap(), - documentRef.ios, - ).let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ): NativeTransactionWrapper = native.updateData( - encodedFieldsAndValues.toMap(), + encodedFieldsAndValues.toEncodedMap(), documentRef.ios, ).let { this } diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt index f1551cecf..ef8899553 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt @@ -1,10 +1,10 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeWriteBatch import dev.gitlive.firebase.firestore.await import dev.gitlive.firebase.firestore.ios +import dev.gitlive.firebase.firestore.toEncodedMap import dev.gitlive.firebase.internal.EncodedObject import dev.gitlive.firebase.internal.ios @@ -23,19 +23,11 @@ internal actual class NativeWriteBatchWrapper actual constructor(actual val nati actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeWriteBatchWrapper = native.updateData(encodedData.ios, documentRef.ios).let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, + encodedFieldsAndValues: List, ): NativeWriteBatchWrapper = native.updateData( - encodedFieldsAndValues.toMap(), - documentRef.ios, - ).let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ): NativeWriteBatchWrapper = native.updateData( - encodedFieldsAndValues.toMap(), + encodedFieldsAndValues.toEncodedMap(), documentRef.ios, ).let { this } diff --git a/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt b/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt index e7c6fc04f..494225234 100644 --- a/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt +++ b/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt @@ -121,28 +121,38 @@ class ContextSwitchTest { }, ) { data -> - fun getDocument() = firestore.collection("fieldValuesOps") + val doc = firestore.collection("fieldValuesOps") .document("fieldValuesOps") - // store - getDocument().set(strategy = TestData.serializer(), data = TestData(data.initial), merge = false) - - // append & verify - getDocument().update(data.updates[0].op) - - var savedData = getDocument().get().data(TestData.serializer()) - assertEquals(data.updates[0].expected, savedData.values) - - // remove & verify - getDocument().update(data.updates[1].op) - savedData = getDocument().get().data(TestData.serializer()) - assertEquals(data.updates[1].expected, savedData.values) + try { + // store + doc.set( + strategy = TestData.serializer(), + data = TestData(data.initial), + merge = false, + ) - val list = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) - assertEquals(data.updates[1].expected, list) - // delete & verify - getDocument().update(data.updates[2].op) - val deletedList = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) - assertEquals(data.updates[2].expected, deletedList) + // append & verify + doc.update(data.updates[0].op) + + var savedData = doc.get().data(TestData.serializer()) + assertEquals(data.updates[0].expected, savedData.values) + + // remove & verify + doc.update(data.updates[1].op) + savedData = doc.get().data(TestData.serializer()) + assertEquals(data.updates[1].expected, savedData.values) + + val list = doc.get() + .get(TestData::values.name, ListSerializer(Int.serializer()).nullable) + assertEquals(data.updates[1].expected, list) + // delete & verify + doc.update(data.updates[2].op) + val deletedList = doc.get() + .get(TestData::values.name, ListSerializer(Int.serializer()).nullable) + assertEquals(data.updates[2].expected, deletedList) + } finally { + doc.delete() + } } } diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index 5065defa2..ac72e14af 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -1,6 +1,5 @@ package dev.gitlive.firebase.firestore.internal -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeCollectionReference import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot @@ -74,25 +73,21 @@ internal actual class NativeDocumentReference actual constructor(actual val nati ).await() } - actual suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) { + actual suspend fun updateEncoded(encodedFieldsAndValues: List) { rethrow { encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() } - ?.performUpdate { field, value, moreFieldsAndValues -> - updateDoc(js, field, value, *moreFieldsAndValues) - } + ?.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + updateDoc(js, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + updateDoc(js, fieldPath, value, *moreFieldsAndValues) + }, + ) ?.await() } } - actual suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) { - rethrow { - encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() } - ?.performUpdate { field, value, moreFieldsAndValues -> - updateDoc(js, field, value, *moreFieldsAndValues) - }?.await() - } - } - actual suspend fun delete() = rethrow { deleteDoc(js).await() } override fun equals(other: Any?): Boolean = diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt index bc49b3437..04327dfd1 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt @@ -45,17 +45,17 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is Filter.Or -> or(*filters.map { it.toQueryConstraint() }.toTypedArray()) is Filter.Field -> { val value = when (constraint) { - is WhereConstraint.ForNullableObject -> constraint.safeValue - is WhereConstraint.ForObject -> constraint.safeValue - is WhereConstraint.ForArray -> constraint.safeValues.toTypedArray() + is WhereConstraint.ForNullableObject -> constraint.value + is WhereConstraint.ForObject -> constraint.value + is WhereConstraint.ForArray -> constraint.values.toTypedArray() } dev.gitlive.firebase.firestore.externals.where(field, constraint.filterOp, value) } is Filter.Path -> { val value = when (constraint) { - is WhereConstraint.ForNullableObject -> constraint.safeValue - is WhereConstraint.ForObject -> constraint.safeValue - is WhereConstraint.ForArray -> constraint.safeValues.toTypedArray() + is WhereConstraint.ForNullableObject -> constraint.value + is WhereConstraint.ForObject -> constraint.value + is WhereConstraint.ForArray -> constraint.values.toTypedArray() } dev.gitlive.firebase.firestore.externals.where(path.js, constraint.filterOp, value) } diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt index 59ba9896d..f12b9d637 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeTransaction import dev.gitlive.firebase.firestore.externals.Transaction import dev.gitlive.firebase.firestore.js @@ -29,22 +28,18 @@ internal actual class NativeTransactionWrapper internal actual constructor(actua actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeTransactionWrapper = rethrow { js.update(documentRef.js, encodedData.js) } .let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, + encodedFieldsAndValues: List, ): NativeTransactionWrapper = rethrow { - encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - js.update(documentRef.js, field, value, *moreFieldsAndValues) - } - }.let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ): NativeTransactionWrapper = rethrow { - encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - js.update(documentRef.js, field, value, *moreFieldsAndValues) - } + encodedFieldsAndValues.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + js.update(documentRef.js, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + js.update(documentRef.js, fieldPath, value, *moreFieldsAndValues) + }, + ) }.let { this } actual fun delete(documentRef: DocumentReference) = diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt index e97cc31ab..45515bdcc 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeWriteBatch import dev.gitlive.firebase.firestore.externals.WriteBatch import dev.gitlive.firebase.firestore.js @@ -26,22 +25,18 @@ internal actual class NativeWriteBatchWrapper internal actual constructor(actual actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeWriteBatchWrapper = rethrow { js.update(documentRef.js, encodedData.js) } .let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, + encodedFieldsAndValues: List, ): NativeWriteBatchWrapper = rethrow { - encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - js.update(documentRef.js, field, value, *moreFieldsAndValues) - } - }.let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ): NativeWriteBatchWrapper = rethrow { - encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - js.update(documentRef.js, field, value, *moreFieldsAndValues) - } + encodedFieldsAndValues.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + js.update(documentRef.js, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + js.update(documentRef.js, fieldPath, value, *moreFieldsAndValues) + }, + ) }.let { this } actual fun delete(documentRef: DocumentReference) =