diff --git a/build.gradle.kts b/build.gradle.kts index f78a4b8ae..52d2d89c1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,7 +32,7 @@ buildscript { val compileSdkVersion by extra(34) val targetSdkVersion by extra(34) -val minSdkVersion by extra(21) +val minSdkVersion by extra(23) tasks { register("updateVersions") { diff --git a/firebase-analytics/src/commonTest/kotlin/dev/gitlive/firebase/analytics/analytics.kt b/firebase-analytics/src/commonTest/kotlin/dev/gitlive/firebase/analytics/analytics.kt index 9f1f5c529..b9708bc13 100644 --- a/firebase-analytics/src/commonTest/kotlin/dev/gitlive/firebase/analytics/analytics.kt +++ b/firebase-analytics/src/commonTest/kotlin/dev/gitlive/firebase/analytics/analytics.kt @@ -9,6 +9,7 @@ import dev.gitlive.firebase.FirebaseOptions import dev.gitlive.firebase.apps import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runBlockingTest +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -27,7 +28,7 @@ class FirebaseAnalyticsTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-app/src/appleMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/appleMain/kotlin/dev/gitlive/firebase/firebase.kt index 6a8411792..fb40f1263 100644 --- a/firebase-app/src/appleMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/appleMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -29,7 +29,7 @@ public actual data class FirebaseApp internal constructor(internal val ios: FIRA actual val name: String get() = ios.name actual val options: FirebaseOptions - get() = ios.options.run { FirebaseOptions(bundleID, APIKey!!, databaseURL!!, trackingID, storageBucket, projectID, GCMSenderID) } + get() = ios.options.run { FirebaseOptions(bundleID, APIKey!!, databaseURL!!, null, storageBucket, projectID, GCMSenderID) } public actual suspend fun delete() { val deleted = CompletableDeferred() @@ -46,7 +46,6 @@ public actual fun Firebase.apps(context: Any?): List = FIRApp.allAp private fun FirebaseOptions.toIos() = FIROptions(this@toIos.applicationId, this@toIos.gcmSenderId ?: "").apply { APIKey = this@toIos.apiKey databaseURL = this@toIos.databaseUrl - trackingID = this@toIos.gaTrackingId storageBucket = this@toIos.storageBucket projectID = this@toIos.projectId } diff --git a/firebase-app/src/commonTest/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/commonTest/kotlin/dev/gitlive/firebase/firebase.kt index 7fac32601..8ac9aa866 100644 --- a/firebase-app/src/commonTest/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/commonTest/kotlin/dev/gitlive/firebase/firebase.kt @@ -1,5 +1,6 @@ package dev.gitlive.firebase +import kotlin.random.Random import kotlin.test.Test import kotlin.test.assertEquals @@ -13,7 +14,7 @@ class FirebaseAppTest { Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 54c641c7f..ab5225502 100644 --- a/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/androidMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -91,18 +91,25 @@ public actual class FirebaseAuth internal constructor(internal val android: com. @Suppress("UNCHECKED_CAST") return when (result.operation) { SIGN_IN_WITH_EMAIL_LINK -> ActionCodeResult.SignInWithEmailLink + VERIFY_EMAIL -> ActionCodeResult.VerifyEmail(result.info!!.email) + PASSWORD_RESET -> ActionCodeResult.PasswordReset(result.info!!.email) + RECOVER_EMAIL -> (result.info as ActionCodeEmailInfo).run { ActionCodeResult.RecoverEmail(email, previousEmail) } + VERIFY_BEFORE_CHANGE_EMAIL -> (result.info as ActionCodeEmailInfo).run { ActionCodeResult.VerifyBeforeChangeEmail(email, previousEmail) } + REVERT_SECOND_FACTOR_ADDITION -> (result.info as ActionCodeMultiFactorInfo).run { ActionCodeResult.RevertSecondFactorAddition(email, MultiFactorInfo(multiFactorInfo)) } + ERROR -> throw UnsupportedOperationException(result.operation.toString()) + else -> throw UnsupportedOperationException(result.operation.toString()) } as T } diff --git a/firebase-auth/src/appleMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/appleMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 7883034b2..f10606fd0 100644 --- a/firebase-auth/src/appleMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/appleMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -154,7 +154,6 @@ public actual class AuthTokenResult(internal val ios: FIRAuthTokenResult) { internal fun ActionCodeSettings.toIos() = FIRActionCodeSettings().also { it.setURL(NSURL.URLWithString(url)) androidPackageName?.run { it.setAndroidPackageName(packageName, installIfNotAvailable, minimumVersion) } - it.setDynamicLinkDomain(dynamicLinkDomain) it.setLinkDomain(linkDomain) it.setHandleCodeInApp(canHandleCodeInApp) iOSBundleId?.run { it.setIOSBundleID(this) } @@ -266,5 +265,6 @@ private fun NSError.toException() = when (domain) { else -> FirebaseAuthException(toString()) } + else -> FirebaseAuthException(toString()) } diff --git a/firebase-auth/src/commonTest/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/commonTest/kotlin/dev/gitlive/firebase/auth/auth.kt index 96f259176..64d39981b 100644 --- a/firebase-auth/src/commonTest/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/commonTest/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -27,7 +27,7 @@ class FirebaseAuthTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 7a7988f53..06b75e34a 100644 --- a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -104,17 +104,23 @@ public actual class FirebaseAuth internal constructor(internal val js: Auth) { @Suppress("UNCHECKED_CAST") return when (result.operation) { "EMAIL_SIGNIN" -> ActionCodeResult.SignInWithEmailLink + "VERIFY_EMAIL" -> ActionCodeResult.VerifyEmail(result.data.email!!) + "PASSWORD_RESET" -> ActionCodeResult.PasswordReset(result.data.email!!) + "RECOVER_EMAIL" -> ActionCodeResult.RecoverEmail(result.data.email!!, result.data.previousEmail!!) + "VERIFY_AND_CHANGE_EMAIL" -> ActionCodeResult.VerifyBeforeChangeEmail( result.data.email!!, result.data.previousEmail!!, ) + "REVERT_SECOND_FACTOR_ADDITION" -> ActionCodeResult.RevertSecondFactorAddition( result.data.email!!, result.data.multiFactorInfo?.let { MultiFactorInfo(it) }, ) + else -> throw UnsupportedOperationException(result.operation) } as T } @@ -208,26 +214,39 @@ private inline fun rethrow(function: () -> R): R { private fun errorToException(cause: dynamic) = when (val code = cause.code?.toString()?.lowercase()) { "auth/invalid-user-token" -> FirebaseAuthInvalidUserException(code, cause.unsafeCast()) + "auth/requires-recent-login" -> FirebaseAuthRecentLoginRequiredException(code, cause.unsafeCast()) + "auth/user-disabled" -> FirebaseAuthInvalidUserException(code, cause.unsafeCast()) + "auth/user-token-expired" -> FirebaseAuthInvalidUserException(code, cause.unsafeCast()) + "auth/web-storage-unsupported" -> FirebaseAuthWebException(code, cause.unsafeCast()) + "auth/network-request-failed" -> FirebaseNetworkException(code, cause.unsafeCast()) + "auth/timeout" -> FirebaseNetworkException(code, cause.unsafeCast()) + "auth/weak-password" -> FirebaseAuthWeakPasswordException(code, cause.unsafeCast()) + "auth/invalid-credential", "auth/invalid-verification-code", "auth/missing-verification-code", "auth/invalid-verification-id", "auth/missing-verification-id", -> FirebaseAuthInvalidCredentialsException(code, cause.unsafeCast()) + "auth/maximum-second-factor-count-exceeded", "auth/second-factor-already-in-use", -> FirebaseAuthMultiFactorException(code, cause.unsafeCast()) + "auth/credential-already-in-use" -> FirebaseAuthUserCollisionException(code, cause.unsafeCast()) + "auth/email-already-in-use" -> FirebaseAuthUserCollisionException(code, cause.unsafeCast()) + "auth/invalid-email" -> FirebaseAuthEmailException(code, cause.unsafeCast()) -// "auth/app-deleted" -> + + // "auth/app-deleted" -> // "auth/app-not-authorized" -> // "auth/argument-error" -> // "auth/invalid-api-key" -> diff --git a/firebase-auth/src/jvmMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/jvmMain/kotlin/dev/gitlive/firebase/auth/auth.kt index 5c5f1e9e2..217ba7649 100644 --- a/firebase-auth/src/jvmMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/jvmMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -98,18 +98,25 @@ public actual class FirebaseAuth internal constructor(internal val android: com. @Suppress("UNCHECKED_CAST") return when (result.operation) { SIGN_IN_WITH_EMAIL_LINK -> ActionCodeResult.SignInWithEmailLink + VERIFY_EMAIL -> ActionCodeResult.VerifyEmail(result.info!!.email) + PASSWORD_RESET -> ActionCodeResult.PasswordReset(result.info!!.email) + RECOVER_EMAIL -> (result.info as ActionCodeEmailInfo).run { ActionCodeResult.RecoverEmail(email, previousEmail) } + VERIFY_BEFORE_CHANGE_EMAIL -> (result.info as ActionCodeEmailInfo).run { ActionCodeResult.VerifyBeforeChangeEmail(email, previousEmail) } + REVERT_SECOND_FACTOR_ADDITION -> (result.info as ActionCodeMultiFactorInfo).run { ActionCodeResult.RevertSecondFactorAddition(email, MultiFactorInfo(multiFactorInfo)) } + ERROR -> throw UnsupportedOperationException(result.operation.toString()) + else -> throw UnsupportedOperationException(result.operation.toString()) } as T } diff --git a/firebase-common-internal/build.gradle.kts b/firebase-common-internal/build.gradle.kts index a37f706c0..991e5c24c 100644 --- a/firebase-common-internal/build.gradle.kts +++ b/firebase-common-internal/build.gradle.kts @@ -155,7 +155,7 @@ kotlin { if (supportedPlatforms.contains(TargetPlatform.Js)) { getByName("jsMain") { dependencies { - api(npm("firebase", "10.12.2")) + api(npm("firebase", libs.versions.firebase.npm.get())) } } } 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 f43627588..9127dbbde 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 @@ -11,6 +11,7 @@ import kotlinx.serialization.encoding.CompositeDecoder 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] } } @@ -23,6 +24,7 @@ internal actual fun FirebaseDecoderImpl.structureDecoder(descriptor: SerialDescr } is PolymorphicKind -> decodeAsMap(polymorphicIsNested) + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } 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 eb7be3ba2..6d3a09910 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 @@ -13,10 +13,14 @@ internal actual fun FirebaseEncoderImpl.structureEncoder(descriptor: SerialDescr StructureKind.LIST -> mutableListOf() .also { value = it } .let { FirebaseCompositeEncoder(settings) { _, index, value -> it.add(index, value) } } + StructureKind.MAP -> mutableListOf() .let { FirebaseCompositeEncoder(settings, { value = it.chunked(2).associate { (k, v) -> k to v } }) { _, _, value -> it.add(value) } } + StructureKind.CLASS, StructureKind.OBJECT -> encodeAsMap(descriptor) + is PolymorphicKind -> encodeAsMap(descriptor) + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } diff --git a/firebase-common-internal/src/appleMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt b/firebase-common-internal/src/appleMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt index 507bb427f..190f599fa 100644 --- a/firebase-common-internal/src/appleMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt +++ b/firebase-common-internal/src/appleMain/kotlin/dev/gitlive/firebase/internal/_decoders.kt @@ -12,11 +12,15 @@ import kotlin.collections.get 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 { FirebaseCompositeDecoder(it.size, settings) { _, index -> it[index / 2].run { if (index % 2 == 0) key else value } } } + is PolymorphicKind -> decodeAsMap(polymorphicIsNested) + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } diff --git a/firebase-common-internal/src/appleMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt b/firebase-common-internal/src/appleMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt index bab44db4e..2e008bd8f 100644 --- a/firebase-common-internal/src/appleMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt +++ b/firebase-common-internal/src/appleMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt @@ -11,10 +11,14 @@ import kotlin.collections.set 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) } } + StructureKind.CLASS, StructureKind.OBJECT -> encodeAsMap(descriptor) + is PolymorphicKind -> encodeAsMap(descriptor) + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } diff --git a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/EncodedObject.kt b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/EncodedObject.kt index ad90414d6..d57bc083f 100644 --- a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/EncodedObject.kt +++ b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/EncodedObject.kt @@ -9,8 +9,11 @@ public val EncodedObject.js: Json get() = json(*getRaw().entries.map { (key, val internal actual fun Any.asNativeMap(): Map<*, *>? { val json = when (this) { is Number -> null + is Boolean -> null + is String -> null + is Map<*, *> -> { if (keys.all { it is String }) { json(*mapKeys { (key, _) -> key as String }.toList().toTypedArray()) @@ -18,8 +21,11 @@ internal actual fun Any.asNativeMap(): Map<*, *>? { null } } + is Collection<*> -> null + is Array<*> -> null + else -> { @Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE") this as Json 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 5cf456d49..554fc11b8 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 @@ -13,7 +13,9 @@ import kotlin.js.Json 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 { FirebaseCompositeDecoder( it.size, @@ -35,6 +37,7 @@ internal actual fun FirebaseDecoderImpl.structureDecoder(descriptor: SerialDescr } is PolymorphicKind -> decodeAsMap(polymorphicIsNested) + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } 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 091d9c2da..47b8477b5 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 @@ -11,6 +11,7 @@ import kotlin.js.json internal actual fun FirebaseEncoderImpl.structureEncoder(descriptor: SerialDescriptor): FirebaseCompositeEncoder = when (descriptor.kind) { StructureKind.LIST -> encodeAsList(descriptor) + StructureKind.MAP -> { val map = json() var lastKey = "" @@ -23,8 +24,11 @@ internal actual fun FirebaseEncoderImpl.structureEncoder(descriptor: SerialDescr } } } + StructureKind.CLASS, StructureKind.OBJECT -> encodeAsMap(descriptor) + is PolymorphicKind -> encodeAsMap(descriptor) + else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } diff --git a/firebase-common/build.gradle.kts b/firebase-common/build.gradle.kts index ea151f115..172b327fb 100644 --- a/firebase-common/build.gradle.kts +++ b/firebase-common/build.gradle.kts @@ -153,7 +153,7 @@ kotlin { if (supportedPlatforms.contains(TargetPlatform.Js)) { getByName("jsMain") { dependencies { - api(npm("firebase", "10.12.2")) + api(npm("firebase", libs.versions.firebase.npm.get())) } } } diff --git a/firebase-config/src/commonTest/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt b/firebase-config/src/commonTest/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt index 137149289..e726d365b 100644 --- a/firebase-config/src/commonTest/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt +++ b/firebase-config/src/commonTest/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt @@ -10,6 +10,7 @@ import dev.gitlive.firebase.apps import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runTest import kotlinx.datetime.Instant +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Ignore @@ -38,7 +39,7 @@ class FirebaseRemoteConfigTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-crashlytics/src/commonTest/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt b/firebase-crashlytics/src/commonTest/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt index c184ac1ff..248846c1b 100644 --- a/firebase-crashlytics/src/commonTest/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt +++ b/firebase-crashlytics/src/commonTest/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt @@ -11,6 +11,7 @@ import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runBlockingTest import dev.gitlive.firebase.runTest import kotlinx.coroutines.delay +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -30,7 +31,7 @@ class FirebaseCrashlyticsTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyB7pZ7tXymW9WC_ozAppbEs9WBffSmfX9c", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt index 91eb328b5..da8e4125d 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt @@ -14,7 +14,7 @@ public actual class ServerValue internal actual constructor( public actual fun increment(delta: Double): ServerValue = ServerValue(NativeServerValue.increment(delta)) } - override fun equals(other: Any?): Boolean = this === other || other is ServerValue && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is ServerValue && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = "ServerValue($nativeValue)" } diff --git a/firebase-database/src/appleMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt b/firebase-database/src/appleMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt index 35cb37701..e7e70066d 100644 --- a/firebase-database/src/appleMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt +++ b/firebase-database/src/appleMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt @@ -18,7 +18,7 @@ public actual class ServerValue internal actual constructor( public actual fun increment(delta: Double): ServerValue = ServerValue(NativeServerValue.increment(delta as NSNumber)) } - override fun equals(other: Any?): Boolean = this === other || other is ServerValue && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is ServerValue && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = "ServerValue($nativeValue)" } diff --git a/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt index a3f49fda8..37896b2ab 100644 --- a/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import kotlinx.serialization.Serializable import kotlinx.serialization.SerializationStrategy +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -42,7 +43,7 @@ class FirebaseDatabaseTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk-default-rtdb.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt index 19e67f5c3..777dfcb33 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/ServerValue.kt @@ -14,7 +14,7 @@ public actual class ServerValue internal actual constructor( public actual fun increment(delta: Double): ServerValue = ServerValue(jsIncrement(delta)) } - override fun equals(other: Any?): Boolean = this === other || other is ServerValue && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is ServerValue && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = "ServerValue($nativeValue)" } diff --git a/firebase-firestore/api/android/firebase-firestore.api b/firebase-firestore/api/android/firebase-firestore.api index d1fc3fafe..b1cef01c1 100644 --- a/firebase-firestore/api/android/firebase-firestore.api +++ b/firebase-firestore/api/android/firebase-firestore.api @@ -1,3 +1,80 @@ +public abstract class dev/gitlive/firebase/firestore/AggregateField { + public static final field Companion Ldev/gitlive/firebase/firestore/AggregateField$Companion; + public abstract fun getAndroid ()Lcom/google/firebase/firestore/AggregateField; +} + +public final class dev/gitlive/firebase/firestore/AggregateField$Average : dev/gitlive/firebase/firestore/AggregateField { + public fun (Lcom/google/firebase/firestore/AggregateField$AverageAggregateField;)V + public final fun component1 ()Lcom/google/firebase/firestore/AggregateField$AverageAggregateField; + public final fun copy (Lcom/google/firebase/firestore/AggregateField$AverageAggregateField;)Ldev/gitlive/firebase/firestore/AggregateField$Average; + public static synthetic fun copy$default (Ldev/gitlive/firebase/firestore/AggregateField$Average;Lcom/google/firebase/firestore/AggregateField$AverageAggregateField;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/AggregateField$Average; + public fun equals (Ljava/lang/Object;)Z + public fun getAndroid ()Lcom/google/firebase/firestore/AggregateField$AverageAggregateField; + public synthetic fun getAndroid ()Lcom/google/firebase/firestore/AggregateField; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/gitlive/firebase/firestore/AggregateField$Companion { + public final fun average (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/AggregateField$Average; + public final fun average (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/AggregateField$Average; + public final fun count ()Ldev/gitlive/firebase/firestore/AggregateField$Count; + public final fun sum (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/AggregateField$Sum; + public final fun sum (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/AggregateField$Sum; +} + +public final class dev/gitlive/firebase/firestore/AggregateField$Count : dev/gitlive/firebase/firestore/AggregateField { + public static final field INSTANCE Ldev/gitlive/firebase/firestore/AggregateField$Count; + public fun equals (Ljava/lang/Object;)Z + public fun getAndroid ()Lcom/google/firebase/firestore/AggregateField$CountAggregateField; + public synthetic fun getAndroid ()Lcom/google/firebase/firestore/AggregateField; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/gitlive/firebase/firestore/AggregateField$Sum : dev/gitlive/firebase/firestore/AggregateField { + public fun (Lcom/google/firebase/firestore/AggregateField$SumAggregateField;)V + public final fun component1 ()Lcom/google/firebase/firestore/AggregateField$SumAggregateField; + public final fun copy (Lcom/google/firebase/firestore/AggregateField$SumAggregateField;)Ldev/gitlive/firebase/firestore/AggregateField$Sum; + public static synthetic fun copy$default (Ldev/gitlive/firebase/firestore/AggregateField$Sum;Lcom/google/firebase/firestore/AggregateField$SumAggregateField;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/AggregateField$Sum; + public fun equals (Ljava/lang/Object;)Z + public fun getAndroid ()Lcom/google/firebase/firestore/AggregateField$SumAggregateField; + public synthetic fun getAndroid ()Lcom/google/firebase/firestore/AggregateField; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/gitlive/firebase/firestore/AggregateQuery { + public static final field Companion Ldev/gitlive/firebase/firestore/AggregateQuery$Companion; + public final fun get (Ldev/gitlive/firebase/firestore/AggregateSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun get$default (Ldev/gitlive/firebase/firestore/AggregateQuery;Ldev/gitlive/firebase/firestore/AggregateSource;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public final fun getQuery ()Ldev/gitlive/firebase/firestore/Query; +} + +public final class dev/gitlive/firebase/firestore/AggregateQuery$Companion { +} + +public final class dev/gitlive/firebase/firestore/AggregateQuerySnapshot { + public static final field Companion Ldev/gitlive/firebase/firestore/AggregateQuerySnapshot$Companion; + public final fun get (Ldev/gitlive/firebase/firestore/AggregateField$Average;)Ljava/lang/Double; + public final fun get (Ldev/gitlive/firebase/firestore/AggregateField$Count;)J + public final fun get (Ldev/gitlive/firebase/firestore/AggregateField;)Ljava/lang/Number; + public final fun getCount ()J + public final fun getDouble (Ldev/gitlive/firebase/firestore/AggregateField;)Ljava/lang/Double; + public final fun getLong (Ldev/gitlive/firebase/firestore/AggregateField;)Ljava/lang/Long; + public final fun getQuery ()Ldev/gitlive/firebase/firestore/AggregateQuery; +} + +public final class dev/gitlive/firebase/firestore/AggregateQuerySnapshot$Companion { +} + +public final class dev/gitlive/firebase/firestore/AggregateSource : java/lang/Enum { + public static final field SERVER Ldev/gitlive/firebase/firestore/AggregateSource; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/AggregateSource; + public static fun values ()[Ldev/gitlive/firebase/firestore/AggregateSource; +} + public abstract class dev/gitlive/firebase/firestore/BaseTimestamp { public static final field Companion Ldev/gitlive/firebase/firestore/BaseTimestamp$Companion; } @@ -65,8 +142,9 @@ public final class dev/gitlive/firebase/firestore/DocumentReference { public static synthetic fun set$default (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;[Ldev/gitlive/firebase/firestore/FieldPath;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static synthetic fun set$default (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;[Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun setEncoded (Ldev/gitlive/firebase/internal/EncodedObject;Ldev/gitlive/firebase/firestore/internal/SetOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun snapshots (Ldev/gitlive/firebase/firestore/SnapshotListenOptions;)Lkotlinx/coroutines/flow/Flow; public final fun snapshots (Z)Lkotlinx/coroutines/flow/Flow; - public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/DocumentReference;ZILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; + public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/firestore/SnapshotListenOptions;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; public fun toString ()Ljava/lang/String; public final fun update (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun update (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -366,6 +444,14 @@ 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/ListenSource : java/lang/Enum { + public static final field CACHE Ldev/gitlive/firebase/firestore/ListenSource; + public static final field DEFAULT Ldev/gitlive/firebase/firestore/ListenSource; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/ListenSource; + public static fun values ()[Ldev/gitlive/firebase/firestore/ListenSource; +} + public abstract interface class dev/gitlive/firebase/firestore/LocalCacheSettings { } @@ -448,8 +534,18 @@ public final class dev/gitlive/firebase/firestore/MemoryGarbageCollectorSettings public final fun newBuilder ()Ldev/gitlive/firebase/firestore/MemoryGarbageCollectorSettings$LRUGC$Builder; } +public final class dev/gitlive/firebase/firestore/MetadataChanges : java/lang/Enum { + public static final field EXCLUDE Ldev/gitlive/firebase/firestore/MetadataChanges; + public static final field INCLUDE Ldev/gitlive/firebase/firestore/MetadataChanges; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/MetadataChanges; + public static fun values ()[Ldev/gitlive/firebase/firestore/MetadataChanges; +} + public class dev/gitlive/firebase/firestore/Query { public static final field Companion Ldev/gitlive/firebase/firestore/Query$Companion; + public final fun aggregate (Ldev/gitlive/firebase/firestore/AggregateField;[Ldev/gitlive/firebase/firestore/AggregateField;)Ldev/gitlive/firebase/firestore/AggregateQuery; + public final fun count ()Ldev/gitlive/firebase/firestore/AggregateQuery; 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; @@ -462,12 +558,14 @@ public class dev/gitlive/firebase/firestore/Query { 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; public final fun limit (Ljava/lang/Number;)Ldev/gitlive/firebase/firestore/Query; + public final fun limitToLast (Ljava/lang/Number;)Ldev/gitlive/firebase/firestore/Query; public final fun orderBy (Ldev/gitlive/firebase/firestore/FieldPath;Lcom/google/firebase/firestore/Query$Direction;)Ldev/gitlive/firebase/firestore/Query; public final fun orderBy (Ljava/lang/String;Lcom/google/firebase/firestore/Query$Direction;)Ldev/gitlive/firebase/firestore/Query; public static synthetic fun orderBy$default (Ldev/gitlive/firebase/firestore/Query;Ldev/gitlive/firebase/firestore/FieldPath;Lcom/google/firebase/firestore/Query$Direction;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public static synthetic fun orderBy$default (Ldev/gitlive/firebase/firestore/Query;Ljava/lang/String;Lcom/google/firebase/firestore/Query$Direction;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun snapshots (Ldev/gitlive/firebase/firestore/SnapshotListenOptions;)Lkotlinx/coroutines/flow/Flow; public final fun snapshots (Z)Lkotlinx/coroutines/flow/Flow; - public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/Query;ZILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; + public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/Query;Ldev/gitlive/firebase/firestore/SnapshotListenOptions;ILjava/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; @@ -507,6 +605,42 @@ public final class dev/gitlive/firebase/firestore/ServerTimestampSerializer : ko public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } +public final class dev/gitlive/firebase/firestore/SnapshotListenOptions { + public fun (Ldev/gitlive/firebase/firestore/ListenSource;Ldev/gitlive/firebase/firestore/MetadataChanges;Landroid/app/Activity;Ljava/util/concurrent/Executor;)V + public final fun component1 ()Ldev/gitlive/firebase/firestore/ListenSource; + public final fun component2 ()Ldev/gitlive/firebase/firestore/MetadataChanges; + public final fun component3 ()Landroid/app/Activity; + public final fun component4 ()Ljava/util/concurrent/Executor; + public final fun copy (Ldev/gitlive/firebase/firestore/ListenSource;Ldev/gitlive/firebase/firestore/MetadataChanges;Landroid/app/Activity;Ljava/util/concurrent/Executor;)Ldev/gitlive/firebase/firestore/SnapshotListenOptions; + public static synthetic fun copy$default (Ldev/gitlive/firebase/firestore/SnapshotListenOptions;Ldev/gitlive/firebase/firestore/ListenSource;Ldev/gitlive/firebase/firestore/MetadataChanges;Landroid/app/Activity;Ljava/util/concurrent/Executor;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/SnapshotListenOptions; + public fun equals (Ljava/lang/Object;)Z + public final fun getActivity ()Landroid/app/Activity; + public final fun getAndroid ()Lcom/google/firebase/firestore/SnapshotListenOptions; + public final fun getExecutor ()Ljava/util/concurrent/Executor; + public final fun getListenSource ()Ldev/gitlive/firebase/firestore/ListenSource; + public final fun getMetadataChanges ()Ldev/gitlive/firebase/firestore/MetadataChanges; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/gitlive/firebase/firestore/SnapshotListenOptions$Builder { + public fun ()V + public final fun build ()Ldev/gitlive/firebase/firestore/SnapshotListenOptions; + public final fun getActivity ()Landroid/app/Activity; + public final fun getExecutor ()Ljava/util/concurrent/Executor; + public final fun getListenSource ()Ldev/gitlive/firebase/firestore/ListenSource; + public final fun getMetadataChanges ()Ldev/gitlive/firebase/firestore/MetadataChanges; + public final fun setActivity (Landroid/app/Activity;)V + public final fun setExecutor (Ljava/util/concurrent/Executor;)V + public final fun setListenSource (Ldev/gitlive/firebase/firestore/ListenSource;)V + public final fun setMetadataChanges (Ldev/gitlive/firebase/firestore/MetadataChanges;)V +} + +public final class dev/gitlive/firebase/firestore/SnapshotListenOptionsKt { + public static final fun snapshotListenOptions (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/SnapshotListenOptions; + public static synthetic fun snapshotListenOptions$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/SnapshotListenOptions; +} + public final class dev/gitlive/firebase/firestore/SnapshotMetadata { public fun (Lcom/google/firebase/firestore/SnapshotMetadata;)V public final fun getHasPendingWrites ()Z @@ -742,6 +876,8 @@ public final class dev/gitlive/firebase/firestore/android { public static synthetic fun firestore$default (Ldev/gitlive/firebase/Firebase;Ldev/gitlive/firebase/FirebaseApp;Ljava/lang/String;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/FirebaseFirestore; public static final fun firestoreSettings (Ldev/gitlive/firebase/firestore/FirebaseFirestoreSettings;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/FirebaseFirestoreSettings; public static synthetic fun firestoreSettings$default (Ldev/gitlive/firebase/firestore/FirebaseFirestoreSettings;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/FirebaseFirestoreSettings; + public static final fun getAndroid (Ldev/gitlive/firebase/firestore/AggregateQuery;)Lcom/google/firebase/firestore/AggregateQuery; + public static final fun getAndroid (Ldev/gitlive/firebase/firestore/AggregateQuerySnapshot;)Lcom/google/firebase/firestore/AggregateQuerySnapshot; public static final fun getAndroid (Ldev/gitlive/firebase/firestore/CollectionReference;)Lcom/google/firebase/firestore/CollectionReference; public static final fun getAndroid (Ldev/gitlive/firebase/firestore/DocumentChange;)Lcom/google/firebase/firestore/DocumentChange; public static final fun getAndroid (Ldev/gitlive/firebase/firestore/DocumentReference;)Lcom/google/firebase/firestore/DocumentReference; @@ -756,6 +892,8 @@ public final class dev/gitlive/firebase/firestore/android { public static final fun getAndroid (Ldev/gitlive/firebase/firestore/WriteBatch;)Lcom/google/firebase/firestore/WriteBatch; public static final fun getCode (Lcom/google/firebase/firestore/FirebaseFirestoreException;)Lcom/google/firebase/firestore/FirebaseFirestoreException$Code; public static final fun getFirestore (Ldev/gitlive/firebase/Firebase;)Ldev/gitlive/firebase/firestore/FirebaseFirestore; + public static final fun invoke (Ldev/gitlive/firebase/firestore/AggregateQuery$Companion;Lcom/google/firebase/firestore/Query;)Ldev/gitlive/firebase/firestore/AggregateQuery; + public static final fun invoke (Ldev/gitlive/firebase/firestore/AggregateQuerySnapshot$Companion;Lcom/google/firebase/firestore/AggregateQuerySnapshot;)Ldev/gitlive/firebase/firestore/AggregateQuerySnapshot; public static final fun invoke (Ldev/gitlive/firebase/firestore/CollectionReference$Companion;Lcom/google/firebase/firestore/CollectionReference;)Ldev/gitlive/firebase/firestore/CollectionReference; public static final fun invoke (Ldev/gitlive/firebase/firestore/DocumentReference$Companion;Lcom/google/firebase/firestore/DocumentReference;)Ldev/gitlive/firebase/firestore/DocumentReference; public static final fun invoke (Ldev/gitlive/firebase/firestore/DocumentSnapshot$Companion;Lcom/google/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/DocumentSnapshot; diff --git a/firebase-firestore/api/jvm/firebase-firestore.api b/firebase-firestore/api/jvm/firebase-firestore.api index 2258747e5..8cbeb8ce0 100644 --- a/firebase-firestore/api/jvm/firebase-firestore.api +++ b/firebase-firestore/api/jvm/firebase-firestore.api @@ -1,3 +1,80 @@ +public abstract class dev/gitlive/firebase/firestore/AggregateField { + public static final field Companion Ldev/gitlive/firebase/firestore/AggregateField$Companion; + public abstract fun getAndroid ()Lcom/google/firebase/firestore/AggregateField; +} + +public final class dev/gitlive/firebase/firestore/AggregateField$Average : dev/gitlive/firebase/firestore/AggregateField { + public fun (Lcom/google/firebase/firestore/AggregateField$AverageAggregateField;)V + public final fun component1 ()Lcom/google/firebase/firestore/AggregateField$AverageAggregateField; + public final fun copy (Lcom/google/firebase/firestore/AggregateField$AverageAggregateField;)Ldev/gitlive/firebase/firestore/AggregateField$Average; + public static synthetic fun copy$default (Ldev/gitlive/firebase/firestore/AggregateField$Average;Lcom/google/firebase/firestore/AggregateField$AverageAggregateField;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/AggregateField$Average; + public fun equals (Ljava/lang/Object;)Z + public fun getAndroid ()Lcom/google/firebase/firestore/AggregateField$AverageAggregateField; + public synthetic fun getAndroid ()Lcom/google/firebase/firestore/AggregateField; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/gitlive/firebase/firestore/AggregateField$Companion { + public final fun average (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/AggregateField$Average; + public final fun average (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/AggregateField$Average; + public final fun count ()Ldev/gitlive/firebase/firestore/AggregateField$Count; + public final fun sum (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/AggregateField$Sum; + public final fun sum (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/AggregateField$Sum; +} + +public final class dev/gitlive/firebase/firestore/AggregateField$Count : dev/gitlive/firebase/firestore/AggregateField { + public static final field INSTANCE Ldev/gitlive/firebase/firestore/AggregateField$Count; + public fun equals (Ljava/lang/Object;)Z + public fun getAndroid ()Lcom/google/firebase/firestore/AggregateField$CountAggregateField; + public synthetic fun getAndroid ()Lcom/google/firebase/firestore/AggregateField; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/gitlive/firebase/firestore/AggregateField$Sum : dev/gitlive/firebase/firestore/AggregateField { + public fun (Lcom/google/firebase/firestore/AggregateField$SumAggregateField;)V + public final fun component1 ()Lcom/google/firebase/firestore/AggregateField$SumAggregateField; + public final fun copy (Lcom/google/firebase/firestore/AggregateField$SumAggregateField;)Ldev/gitlive/firebase/firestore/AggregateField$Sum; + public static synthetic fun copy$default (Ldev/gitlive/firebase/firestore/AggregateField$Sum;Lcom/google/firebase/firestore/AggregateField$SumAggregateField;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/AggregateField$Sum; + public fun equals (Ljava/lang/Object;)Z + public fun getAndroid ()Lcom/google/firebase/firestore/AggregateField$SumAggregateField; + public synthetic fun getAndroid ()Lcom/google/firebase/firestore/AggregateField; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/gitlive/firebase/firestore/AggregateQuery { + public static final field Companion Ldev/gitlive/firebase/firestore/AggregateQuery$Companion; + public final fun get (Ldev/gitlive/firebase/firestore/AggregateSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun get$default (Ldev/gitlive/firebase/firestore/AggregateQuery;Ldev/gitlive/firebase/firestore/AggregateSource;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public final fun getQuery ()Ldev/gitlive/firebase/firestore/Query; +} + +public final class dev/gitlive/firebase/firestore/AggregateQuery$Companion { +} + +public final class dev/gitlive/firebase/firestore/AggregateQuerySnapshot { + public static final field Companion Ldev/gitlive/firebase/firestore/AggregateQuerySnapshot$Companion; + public final fun get (Ldev/gitlive/firebase/firestore/AggregateField$Average;)Ljava/lang/Double; + public final fun get (Ldev/gitlive/firebase/firestore/AggregateField$Count;)J + public final fun get (Ldev/gitlive/firebase/firestore/AggregateField;)Ljava/lang/Number; + public final fun getCount ()J + public final fun getDouble (Ldev/gitlive/firebase/firestore/AggregateField;)Ljava/lang/Double; + public final fun getLong (Ldev/gitlive/firebase/firestore/AggregateField;)Ljava/lang/Long; + public final fun getQuery ()Ldev/gitlive/firebase/firestore/AggregateQuery; +} + +public final class dev/gitlive/firebase/firestore/AggregateQuerySnapshot$Companion { +} + +public final class dev/gitlive/firebase/firestore/AggregateSource : java/lang/Enum { + public static final field SERVER Ldev/gitlive/firebase/firestore/AggregateSource; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/AggregateSource; + public static fun values ()[Ldev/gitlive/firebase/firestore/AggregateSource; +} + public abstract class dev/gitlive/firebase/firestore/BaseTimestamp { public static final field Companion Ldev/gitlive/firebase/firestore/BaseTimestamp$Companion; } @@ -65,8 +142,9 @@ public final class dev/gitlive/firebase/firestore/DocumentReference { public static synthetic fun set$default (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;[Ldev/gitlive/firebase/firestore/FieldPath;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static synthetic fun set$default (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;[Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun setEncoded (Ldev/gitlive/firebase/internal/EncodedObject;Ldev/gitlive/firebase/firestore/internal/SetOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun snapshots (Ldev/gitlive/firebase/firestore/SnapshotListenOptions;)Lkotlinx/coroutines/flow/Flow; public final fun snapshots (Z)Lkotlinx/coroutines/flow/Flow; - public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/DocumentReference;ZILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; + public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/firestore/SnapshotListenOptions;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; public fun toString ()Ljava/lang/String; public final fun update (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun update (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -366,6 +444,14 @@ 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/ListenSource : java/lang/Enum { + public static final field CACHE Ldev/gitlive/firebase/firestore/ListenSource; + public static final field DEFAULT Ldev/gitlive/firebase/firestore/ListenSource; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/ListenSource; + public static fun values ()[Ldev/gitlive/firebase/firestore/ListenSource; +} + public abstract interface class dev/gitlive/firebase/firestore/LocalCacheSettings { } @@ -448,8 +534,18 @@ public final class dev/gitlive/firebase/firestore/MemoryGarbageCollectorSettings public final fun newBuilder ()Ldev/gitlive/firebase/firestore/MemoryGarbageCollectorSettings$LRUGC$Builder; } +public final class dev/gitlive/firebase/firestore/MetadataChanges : java/lang/Enum { + public static final field EXCLUDE Ldev/gitlive/firebase/firestore/MetadataChanges; + public static final field INCLUDE Ldev/gitlive/firebase/firestore/MetadataChanges; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/MetadataChanges; + public static fun values ()[Ldev/gitlive/firebase/firestore/MetadataChanges; +} + public class dev/gitlive/firebase/firestore/Query { public static final field Companion Ldev/gitlive/firebase/firestore/Query$Companion; + public final fun aggregate (Ldev/gitlive/firebase/firestore/AggregateField;[Ldev/gitlive/firebase/firestore/AggregateField;)Ldev/gitlive/firebase/firestore/AggregateQuery; + public final fun count ()Ldev/gitlive/firebase/firestore/AggregateQuery; 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; @@ -462,12 +558,14 @@ public class dev/gitlive/firebase/firestore/Query { 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; public final fun limit (Ljava/lang/Number;)Ldev/gitlive/firebase/firestore/Query; + public final fun limitToLast (Ljava/lang/Number;)Ldev/gitlive/firebase/firestore/Query; public final fun orderBy (Ldev/gitlive/firebase/firestore/FieldPath;Lcom/google/firebase/firestore/Query$Direction;)Ldev/gitlive/firebase/firestore/Query; public final fun orderBy (Ljava/lang/String;Lcom/google/firebase/firestore/Query$Direction;)Ldev/gitlive/firebase/firestore/Query; public static synthetic fun orderBy$default (Ldev/gitlive/firebase/firestore/Query;Ldev/gitlive/firebase/firestore/FieldPath;Lcom/google/firebase/firestore/Query$Direction;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public static synthetic fun orderBy$default (Ldev/gitlive/firebase/firestore/Query;Ljava/lang/String;Lcom/google/firebase/firestore/Query$Direction;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; + public final fun snapshots (Ldev/gitlive/firebase/firestore/SnapshotListenOptions;)Lkotlinx/coroutines/flow/Flow; public final fun snapshots (Z)Lkotlinx/coroutines/flow/Flow; - public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/Query;ZILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; + public static synthetic fun snapshots$default (Ldev/gitlive/firebase/firestore/Query;Ldev/gitlive/firebase/firestore/SnapshotListenOptions;ILjava/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; @@ -507,6 +605,42 @@ public final class dev/gitlive/firebase/firestore/ServerTimestampSerializer : ko public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } +public final class dev/gitlive/firebase/firestore/SnapshotListenOptions { + public fun (Ldev/gitlive/firebase/firestore/ListenSource;Ldev/gitlive/firebase/firestore/MetadataChanges;Landroid/app/Activity;Ljava/util/concurrent/Executor;)V + public final fun component1 ()Ldev/gitlive/firebase/firestore/ListenSource; + public final fun component2 ()Ldev/gitlive/firebase/firestore/MetadataChanges; + public final fun component3 ()Landroid/app/Activity; + public final fun component4 ()Ljava/util/concurrent/Executor; + public final fun copy (Ldev/gitlive/firebase/firestore/ListenSource;Ldev/gitlive/firebase/firestore/MetadataChanges;Landroid/app/Activity;Ljava/util/concurrent/Executor;)Ldev/gitlive/firebase/firestore/SnapshotListenOptions; + public static synthetic fun copy$default (Ldev/gitlive/firebase/firestore/SnapshotListenOptions;Ldev/gitlive/firebase/firestore/ListenSource;Ldev/gitlive/firebase/firestore/MetadataChanges;Landroid/app/Activity;Ljava/util/concurrent/Executor;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/SnapshotListenOptions; + public fun equals (Ljava/lang/Object;)Z + public final fun getActivity ()Landroid/app/Activity; + public final fun getAndroid ()Lcom/google/firebase/firestore/SnapshotListenOptions; + public final fun getExecutor ()Ljava/util/concurrent/Executor; + public final fun getListenSource ()Ldev/gitlive/firebase/firestore/ListenSource; + public final fun getMetadataChanges ()Ldev/gitlive/firebase/firestore/MetadataChanges; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/gitlive/firebase/firestore/SnapshotListenOptions$Builder { + public fun ()V + public final fun build ()Ldev/gitlive/firebase/firestore/SnapshotListenOptions; + public final fun getActivity ()Landroid/app/Activity; + public final fun getExecutor ()Ljava/util/concurrent/Executor; + public final fun getListenSource ()Ldev/gitlive/firebase/firestore/ListenSource; + public final fun getMetadataChanges ()Ldev/gitlive/firebase/firestore/MetadataChanges; + public final fun setActivity (Landroid/app/Activity;)V + public final fun setExecutor (Ljava/util/concurrent/Executor;)V + public final fun setListenSource (Ldev/gitlive/firebase/firestore/ListenSource;)V + public final fun setMetadataChanges (Ldev/gitlive/firebase/firestore/MetadataChanges;)V +} + +public final class dev/gitlive/firebase/firestore/SnapshotListenOptionsKt { + public static final fun snapshotListenOptions (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/SnapshotListenOptions; + public static synthetic fun snapshotListenOptions$default (Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/SnapshotListenOptions; +} + public final class dev/gitlive/firebase/firestore/SnapshotMetadata { public fun (Lcom/google/firebase/firestore/SnapshotMetadata;)V public final fun getHasPendingWrites ()Z @@ -742,6 +876,8 @@ public final class dev/gitlive/firebase/firestore/android { public static synthetic fun firestore$default (Ldev/gitlive/firebase/Firebase;Ldev/gitlive/firebase/FirebaseApp;Ljava/lang/String;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/FirebaseFirestore; public static final fun firestoreSettings (Ldev/gitlive/firebase/firestore/FirebaseFirestoreSettings;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/FirebaseFirestoreSettings; public static synthetic fun firestoreSettings$default (Ldev/gitlive/firebase/firestore/FirebaseFirestoreSettings;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/FirebaseFirestoreSettings; + public static final fun getAndroid (Ldev/gitlive/firebase/firestore/AggregateQuery;)Lcom/google/firebase/firestore/AggregateQuery; + public static final fun getAndroid (Ldev/gitlive/firebase/firestore/AggregateQuerySnapshot;)Lcom/google/firebase/firestore/AggregateQuerySnapshot; public static final fun getAndroid (Ldev/gitlive/firebase/firestore/CollectionReference;)Lcom/google/firebase/firestore/CollectionReference; public static final fun getAndroid (Ldev/gitlive/firebase/firestore/DocumentChange;)Lcom/google/firebase/firestore/DocumentChange; public static final fun getAndroid (Ldev/gitlive/firebase/firestore/DocumentReference;)Lcom/google/firebase/firestore/DocumentReference; @@ -756,6 +892,8 @@ public final class dev/gitlive/firebase/firestore/android { public static final fun getAndroid (Ldev/gitlive/firebase/firestore/WriteBatch;)Lcom/google/firebase/firestore/WriteBatch; public static final fun getCode (Lcom/google/firebase/firestore/FirebaseFirestoreException;)Lcom/google/firebase/firestore/FirebaseFirestoreException$Code; public static final fun getFirestore (Ldev/gitlive/firebase/Firebase;)Ldev/gitlive/firebase/firestore/FirebaseFirestore; + public static final fun invoke (Ldev/gitlive/firebase/firestore/AggregateQuery$Companion;Lcom/google/firebase/firestore/Query;)Ldev/gitlive/firebase/firestore/AggregateQuery; + public static final fun invoke (Ldev/gitlive/firebase/firestore/AggregateQuerySnapshot$Companion;Lcom/google/firebase/firestore/AggregateQuerySnapshot;)Ldev/gitlive/firebase/firestore/AggregateQuerySnapshot; public static final fun invoke (Ldev/gitlive/firebase/firestore/CollectionReference$Companion;Lcom/google/firebase/firestore/CollectionReference;)Ldev/gitlive/firebase/firestore/CollectionReference; public static final fun invoke (Ldev/gitlive/firebase/firestore/DocumentReference$Companion;Lcom/google/firebase/firestore/DocumentReference;)Ldev/gitlive/firebase/firestore/DocumentReference; public static final fun invoke (Ldev/gitlive/firebase/firestore/DocumentSnapshot$Companion;Lcom/google/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/DocumentSnapshot; diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt index c8945c6aa..0ee1adbef 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt @@ -11,7 +11,7 @@ public actual class FieldValue internal actual constructor(internal actual val n init { require(nativeValue is NativeFieldValue) } - override fun equals(other: Any?): Boolean = this === other || other is FieldValue && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is FieldValue && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt index 25326b01a..449708077 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt @@ -12,7 +12,7 @@ public actual class GeoPoint internal actual constructor(internal actual val nat public actual val latitude: Double = nativeValue.latitude public actual val longitude: Double = nativeValue.longitude - override fun equals(other: Any?): Boolean = this === other || other is GeoPoint && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is GeoPoint && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt new file mode 100644 index 000000000..d8d835269 --- /dev/null +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt @@ -0,0 +1,51 @@ +package dev.gitlive.firebase.firestore + +import android.app.Activity +import com.google.android.gms.tasks.TaskExecutors +import java.util.concurrent.Executor +import com.google.firebase.firestore.ListenSource as AndroidListenSource +import com.google.firebase.firestore.MetadataChanges as AndroidMetadataChanges +import com.google.firebase.firestore.SnapshotListenOptions as AndroidSnapshotListenOptions + +public actual data class SnapshotListenOptions( + public actual val listenSource: ListenSource, + public actual val metadataChanges: MetadataChanges, + public val activity: Activity?, + public val executor: Executor, +) { + public actual class Builder actual constructor() { + public actual var listenSource: ListenSource = ListenSource.DEFAULT + public actual var metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE + public var activity: Activity? = null + + public var executor: Executor = TaskExecutors.MAIN_THREAD + + public actual fun build(): SnapshotListenOptions = SnapshotListenOptions( + listenSource = listenSource, + metadataChanges = metadataChanges, + activity = activity, + executor = executor, + ) + } + + public val android: AndroidSnapshotListenOptions = AndroidSnapshotListenOptions.Builder() + .setSource( + when (listenSource) { + ListenSource.DEFAULT -> AndroidListenSource.DEFAULT + ListenSource.CACHE -> AndroidListenSource.CACHE + }, + ) + .setMetadataChanges( + when (metadataChanges) { + MetadataChanges.EXCLUDE -> AndroidMetadataChanges.EXCLUDE + MetadataChanges.INCLUDE -> AndroidMetadataChanges.INCLUDE + }, + ) + .setExecutor(executor) + .apply { + activity?.let { + setActivity(it) + } + } + .build() +} diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt index aa4d76c2f..50c1d761d 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt @@ -21,7 +21,7 @@ public actual class Timestamp internal actual constructor( public actual val seconds: Long = nativeValue.seconds public actual val nanoseconds: Int = nativeValue.nanoseconds - override fun equals(other: Any?): Boolean = this === other || other is Timestamp && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is Timestamp && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt index a1e313dd6..ebd2e5029 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt @@ -7,5 +7,6 @@ internal actual fun isSpecialValue(value: Any): Boolean = when (value) { is NativeTimestamp, is NativeDocumentReferenceType, -> true + else -> false } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index ba5b0fe70..331384a9d 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -12,6 +12,12 @@ import dev.gitlive.firebase.FirebaseApp import dev.gitlive.firebase.android import dev.gitlive.firebase.firestore.internal.NativeDocumentSnapshotWrapper import java.util.concurrent.Executor +import com.google.firebase.firestore.AggregateField as AndroidAggregateField +import com.google.firebase.firestore.AggregateField.AverageAggregateField as AndroidAverageAggregateField +import com.google.firebase.firestore.AggregateField.CountAggregateField as AndroidCountAggregateField +import com.google.firebase.firestore.AggregateField.SumAggregateField as AndroidSumAggregateField +import com.google.firebase.firestore.AggregateQuery as AndroidAggregateQuery +import com.google.firebase.firestore.AggregateQuerySnapshot as AndroidAggregateQuerySnapshot import com.google.firebase.firestore.CollectionReference as AndroidCollectionReference import com.google.firebase.firestore.DocumentChange as AndroidDocumentChange import com.google.firebase.firestore.DocumentReference as AndroidDocumentReference @@ -42,10 +48,12 @@ public val LocalCacheSettings.android: AndroidLocalCacheSettings get() = when (t is LocalCacheSettings.Persistent -> androidPersistentCacheSettings { setSizeBytes(sizeBytes) } + is LocalCacheSettings.Memory -> androidMemoryCacheSettings { setGcSettings( when (garbaseCollectorSettings) { is MemoryGarbageCollectorSettings.Eager -> androidMemoryEagerGcSettings { } + is MemoryGarbageCollectorSettings.LRUGC -> androidMemoryLruGcSettings { setSizeBytes(garbaseCollectorSettings.sizeBytes) } @@ -124,6 +132,15 @@ internal actual typealias NativeQuery = AndroidQuery public operator fun Query.Companion.invoke(android: AndroidQuery): Query = Query(android) public val Query.android: AndroidQuery get() = native +internal actual typealias NativeAggregateQuery = AndroidAggregateQuery +internal actual typealias NativeAggregateQuerySnapshot = AndroidAggregateQuerySnapshot + +public operator fun AggregateQuery.Companion.invoke(android: AndroidQuery): AggregateQuery = AggregateQuery(android) +public val AggregateQuery.android: AndroidAggregateQuery get() = native + +public operator fun AggregateQuerySnapshot.Companion.invoke(android: AndroidAggregateQuerySnapshot): AggregateQuerySnapshot = AggregateQuerySnapshot(android) +public val AggregateQuerySnapshot.android: AndroidAggregateQuerySnapshot get() = native + public actual typealias Direction = AndroidQuery.Direction public actual typealias ChangeType = AndroidDocumentChange.Type @@ -198,3 +215,26 @@ public actual class FieldPath private constructor(internal val android: AndroidF public actual typealias EncodedFieldPath = AndroidFieldPath internal typealias NativeSource = AndroidSource + +public actual sealed class AggregateField { + + public abstract val android: AndroidAggregateField + + public actual companion object { + public actual fun average(field: String): Average = Average(AndroidAggregateField.average(field)) + public actual fun average(fieldPath: FieldPath): Average = Average(AndroidAggregateField.average(fieldPath.android)) + public actual fun count(): Count = Count + public actual fun sum(field: String): Sum = Sum(AndroidAggregateField.sum(field)) + public actual fun sum(fieldPath: FieldPath): Sum = Sum(AndroidAggregateField.sum(fieldPath.android)) + } + + public actual data object Count : AggregateField() { + override val android: AndroidCountAggregateField get() = AndroidAggregateField.count() + } + public actual data class Average( + override val android: AndroidAverageAggregateField, + ) : AggregateField() + public actual data class Sum( + override val android: AndroidSumAggregateField, + ) : AggregateField() +} diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt new file mode 100644 index 000000000..49275ab2d --- /dev/null +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt @@ -0,0 +1,30 @@ +package dev.gitlive.firebase.firestore.internal + +import dev.gitlive.firebase.firestore.AggregateField +import dev.gitlive.firebase.firestore.AggregateSource +import dev.gitlive.firebase.firestore.NativeAggregateQuery +import dev.gitlive.firebase.firestore.NativeAggregateQuerySnapshot +import dev.gitlive.firebase.firestore.NativeQuery +import kotlinx.coroutines.tasks.await + +internal actual class NativeAggregateQueryWrapper actual constructor(actual val native: NativeAggregateQuery) { + + actual val query: NativeQuery get() = native.query + + actual suspend fun get(source: AggregateSource): NativeAggregateQuerySnapshot = native.get( + when (source) { + AggregateSource.SERVER -> com.google.firebase.firestore.AggregateSource.SERVER + }, + ).await() +} + +internal actual class NativeAggregateQuerySnapshotWrapper actual constructor(actual val native: NativeAggregateQuerySnapshot) { + actual val query: NativeAggregateQuery get() = native.query + actual val count: Long get() = native.count + + actual operator fun get(aggregateField: AggregateField): Number? = native.get(aggregateField.android) as? Number + actual operator fun get(averageAggregateField: AggregateField.Average): Double? = native.get(averageAggregateField.android) + actual operator fun get(countAggregateField: AggregateField.Count): Long = native.get(countAggregateField.android) + actual fun getDouble(aggregateField: AggregateField): Double? = native.getDouble(aggregateField.android) + actual fun getLong(aggregateField: AggregateField): Long? = native.getLong(aggregateField.android) +} 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 96dedbfbb..c1d3dae2d 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 @@ -4,6 +4,7 @@ import com.google.android.gms.tasks.TaskExecutors import com.google.firebase.firestore.MetadataChanges import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot +import dev.gitlive.firebase.firestore.SnapshotListenOptions import dev.gitlive.firebase.firestore.Source import dev.gitlive.firebase.firestore.performUpdate import dev.gitlive.firebase.internal.EncodedObject @@ -59,7 +60,16 @@ internal actual class NativeDocumentReference actual constructor(actual val nati exception?.let { close(exception) } } - override fun equals(other: Any?): Boolean = this === other || other is NativeDocumentReference && nativeValue == other.nativeValue + actual fun snapshots(listenOptions: SnapshotListenOptions) = callbackFlow { + val registration = + android.addSnapshotListener(listenOptions.android) { snapshots, exception -> + snapshots?.let { trySend(snapshots) } + exception?.let { close(exception) } + } + awaitClose { registration.remove() } + } + + override fun equals(other: Any?): Boolean = this === other || (other is NativeDocumentReference && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() 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 2ae64e1d2..3c7a85f1d 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 @@ -4,11 +4,14 @@ import com.google.android.gms.tasks.TaskExecutors import com.google.firebase.firestore.FieldPath import com.google.firebase.firestore.MetadataChanges import com.google.firebase.firestore.Query +import dev.gitlive.firebase.firestore.AggregateField import dev.gitlive.firebase.firestore.Direction import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.Filter +import dev.gitlive.firebase.firestore.NativeAggregateQuery import dev.gitlive.firebase.firestore.NativeDocumentSnapshot import dev.gitlive.firebase.firestore.QuerySnapshot +import dev.gitlive.firebase.firestore.SnapshotListenOptions import dev.gitlive.firebase.firestore.Source import dev.gitlive.firebase.firestore.WhereConstraint import kotlinx.coroutines.channels.ProducerScope @@ -19,6 +22,7 @@ import kotlinx.coroutines.tasks.await internal actual open class NativeQueryWrapper internal actual constructor(actual open val native: Query) { actual fun limit(limit: Number) = native.limit(limit.toLong()) + actual fun limitToLast(limit: Number) = native.limitToLast(limit.toLong()) actual val snapshots get() = callbackFlow { val listener = native.addSnapshotListener { snapshot, exception -> @@ -37,6 +41,13 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } awaitClose { listener.remove() } } + actual fun snapshots(listenOptions: SnapshotListenOptions) = callbackFlow { + val listener = native.addSnapshotListener(listenOptions.android) { snapshot, exception -> + snapshot?.let { trySend(QuerySnapshot(snapshot)) } + exception?.let { close(exception) } + } + awaitClose { listener.remove() } + } actual suspend fun get(source: Source): QuerySnapshot = QuerySnapshot(native.get(source.toAndroidSource()).await()) @@ -47,10 +58,12 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual *filters.map { it.toAndroidFilter() } .toTypedArray(), ) + is Filter.Or -> com.google.firebase.firestore.Filter.or( *filters.map { it.toAndroidFilter() } .toTypedArray(), ) + is Filter.Field -> { when (constraint) { is WhereConstraint.ForNullableObject -> { @@ -60,6 +73,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } modifier.invoke(field, constraint.value) } + is WhereConstraint.ForObject -> { val modifier: (String, Any) -> com.google.firebase.firestore.Filter = when (constraint) { is WhereConstraint.LessThan -> com.google.firebase.firestore.Filter::lessThan @@ -70,6 +84,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } modifier.invoke(field, constraint.value) } + is WhereConstraint.ForArray -> { val modifier: (String, List) -> com.google.firebase.firestore.Filter = when (constraint) { is WhereConstraint.InArray -> com.google.firebase.firestore.Filter::inArray @@ -80,6 +95,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } } } + is Filter.Path -> { when (constraint) { is WhereConstraint.ForNullableObject -> { @@ -89,6 +105,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } modifier.invoke(path.android, constraint.value) } + is WhereConstraint.ForObject -> { val modifier: (FieldPath, Any) -> com.google.firebase.firestore.Filter = when (constraint) { is WhereConstraint.LessThan -> com.google.firebase.firestore.Filter::lessThan @@ -99,6 +116,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } modifier.invoke(path.android, constraint.value) } + is WhereConstraint.ForArray -> { val modifier: (FieldPath, List) -> com.google.firebase.firestore.Filter = when (constraint) { is WhereConstraint.InArray -> com.google.firebase.firestore.Filter::inArray @@ -137,4 +155,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } awaitClose { registration.remove() } } + + actual fun count(): NativeAggregateQuery = native.count() + actual fun aggregate(aggregateField: AggregateField, vararg aggregateFields: AggregateField) = native.aggregate(aggregateField.android, *aggregateFields.map { it.android }.toTypedArray()) } diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt index a1e9a49ec..4c0083780 100644 --- a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt @@ -12,7 +12,7 @@ public actual class FieldValue internal actual constructor(internal actual val n init { require(nativeValue is NativeFieldValue) } - override fun equals(other: Any?): Boolean = this === other || other is FieldValue && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is FieldValue && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt index a9bcf2e7f..47272e25a 100644 --- a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt @@ -13,7 +13,7 @@ public actual class GeoPoint internal actual constructor(internal actual val nat public actual val latitude: Double = nativeValue.latitude public actual val longitude: Double = nativeValue.longitude - override fun equals(other: Any?): Boolean = this === other || other is GeoPoint && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is GeoPoint && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() } diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt new file mode 100644 index 000000000..23aa5b600 --- /dev/null +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt @@ -0,0 +1,32 @@ +package dev.gitlive.firebase.firestore + +import cocoapods.FirebaseFirestoreInternal.FIRListenSource +import cocoapods.FirebaseFirestoreInternal.FIRSnapshotListenOptions + +public actual data class SnapshotListenOptions( + public actual val listenSource: ListenSource, + public actual val metadataChanges: MetadataChanges, +) { + public actual class Builder actual constructor() { + public actual var listenSource: ListenSource = ListenSource.DEFAULT + public actual var metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE + + public actual fun build(): SnapshotListenOptions = SnapshotListenOptions( + listenSource = listenSource, + metadataChanges = metadataChanges, + ) + } + + public val ios: FIRSnapshotListenOptions = FIRSnapshotListenOptions().optionsWithSource( + when (listenSource) { + ListenSource.DEFAULT -> FIRListenSource.FIRListenSourceDefault + ListenSource.CACHE -> FIRListenSource.FIRListenSourceCache + }, + ) + .optionsWithIncludeMetadataChanges( + when (metadataChanges) { + MetadataChanges.INCLUDE -> true + MetadataChanges.EXCLUDE -> false + }, + ) +} diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt index 49505e776..3a5550305 100644 --- a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt @@ -20,7 +20,7 @@ public actual class Timestamp internal actual constructor( public actual val seconds: Long = nativeValue.seconds public actual val nanoseconds: Int = nativeValue.nanoseconds - override fun equals(other: Any?): Boolean = this === other || other is Timestamp && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is Timestamp && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt index c2907944a..907f3b920 100644 --- a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt @@ -9,5 +9,6 @@ internal actual fun isSpecialValue(value: Any): Boolean = when (value) { is NativeTimestamp, is NativeDocumentReferenceType, -> true + else -> false } diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 1740ec26e..fba91c5cf 100644 --- a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -31,6 +31,7 @@ public actual fun Firebase.firestore(app: FirebaseApp, databaseId: String?): Fir public val LocalCacheSettings.ios: FIRLocalCacheSettingsProtocol get() = when (this) { is LocalCacheSettings.Persistent -> FIRPersistentCacheSettings(NSNumber.numberWithLong(sizeBytes)) + is LocalCacheSettings.Memory -> FIRMemoryCacheSettings( when (garbaseCollectorSettings) { is MemoryGarbageCollectorSettings.Eager -> FIRMemoryEagerGCSettings() @@ -122,6 +123,15 @@ internal actual typealias NativeQuery = FIRQuery public operator fun Query.Companion.invoke(ios: FIRQuery): Query = Query(ios) public val Query.ios: NativeQuery get() = native +internal actual typealias NativeAggregateQuery = FIRAggregateQuery +internal actual typealias NativeAggregateQuerySnapshot = FIRAggregateQuerySnapshot + +public operator fun AggregateQuery.Companion.invoke(android: FIRQuery): AggregateQuery = AggregateQuery(android) +public val AggregateQuery.ios: FIRAggregateQuery get() = native + +public operator fun AggregateQuerySnapshot.Companion.invoke(android: FIRAggregateQuerySnapshot): AggregateQuerySnapshot = AggregateQuerySnapshot(android) +public val AggregateQuerySnapshot.ios: FIRAggregateQuerySnapshot get() = native + internal actual typealias NativeCollectionReference = FIRCollectionReference public operator fun CollectionReference.Companion.invoke(ios: FIRCollectionReference): CollectionReference = CollectionReference(ios) @@ -185,6 +195,7 @@ public fun NSError.toException(): FirebaseFirestoreException = when (domain) { FIRFirestoreErrorCodeUnauthenticated -> FirestoreExceptionCode.UNAUTHENTICATED else -> FirestoreExceptionCode.UNKNOWN } + else -> FirestoreExceptionCode.UNKNOWN }.let { FirebaseFirestoreException(description!!, it) } @@ -263,3 +274,26 @@ internal suspend inline fun await(function: (callback: (NSError?) -> Unit) - job.await() return result } + +public actual sealed class AggregateField { + + public abstract val ios: FIRAggregateField + + public actual companion object { + public actual fun average(field: String): Average = Average(FIRAggregateField.aggregateFieldForAverageOfField(field)) + public actual fun average(fieldPath: FieldPath): Average = Average(FIRAggregateField.aggregateFieldForAverageOfFieldPath(fieldPath.ios)) + public actual fun count(): Count = Count + public actual fun sum(field: String): Sum = Sum(FIRAggregateField.aggregateFieldForSumOfField(field)) + public actual fun sum(fieldPath: FieldPath): Sum = Sum(FIRAggregateField.aggregateFieldForSumOfFieldPath(fieldPath.ios)) + } + + public actual data object Count : AggregateField() { + override val ios: FIRAggregateField get() = FIRAggregateField.aggregateFieldForCount() + } + public actual data class Average( + override val ios: FIRAggregateField, + ) : AggregateField() + public actual data class Sum( + override val ios: FIRAggregateField, + ) : AggregateField() +} diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt new file mode 100644 index 000000000..cd79a1651 --- /dev/null +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt @@ -0,0 +1,37 @@ +package dev.gitlive.firebase.firestore.internal + +import cocoapods.FirebaseFirestoreInternal.FIRAggregateSource +import dev.gitlive.firebase.firestore.AggregateField +import dev.gitlive.firebase.firestore.AggregateSource +import dev.gitlive.firebase.firestore.NativeAggregateQuery +import dev.gitlive.firebase.firestore.NativeAggregateQuerySnapshot +import dev.gitlive.firebase.firestore.NativeQuery +import dev.gitlive.firebase.firestore.awaitResult +import platform.Foundation.NSNumber + +internal actual class NativeAggregateQueryWrapper actual constructor(actual val native: NativeAggregateQuery) { + + actual val query: NativeQuery get() = native.query + + actual suspend fun get(source: AggregateSource): NativeAggregateQuerySnapshot = awaitResult { callback -> + native.aggregationWithSource( + when (source) { + AggregateSource.SERVER -> FIRAggregateSource.FIRAggregateSourceServer + }, + callback, + ) + } +} + +internal actual class NativeAggregateQuerySnapshotWrapper actual constructor(actual val native: NativeAggregateQuerySnapshot) { + actual val query: NativeAggregateQuery get() = native.query + actual val count: Long get() = native.count().longValue + + @Suppress("CAST_NEVER_SUCCEEDS") // Should succeed just fine + actual operator fun get(aggregateField: AggregateField): Number? = native.valueForAggregateField(aggregateField.ios) as? NSNumber as? Number + actual operator fun get(averageAggregateField: AggregateField.Average): Double? = get(aggregateField = averageAggregateField)?.toDouble() + actual operator fun get(countAggregateField: AggregateField.Count): Long = get(aggregateField = countAggregateField)!!.toLong() + + actual fun getDouble(aggregateField: AggregateField): Double? = get(aggregateField)?.toDouble() + actual fun getLong(aggregateField: AggregateField): Long? = get(aggregateField)?.toLong() +} diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index 29658cacf..b93dafc32 100644 --- a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -1,6 +1,7 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.NativeDocumentReferenceType +import dev.gitlive.firebase.firestore.SnapshotListenOptions import dev.gitlive.firebase.firestore.Source import dev.gitlive.firebase.firestore.await import dev.gitlive.firebase.firestore.awaitResult @@ -22,6 +23,15 @@ internal actual class NativeDocumentReference actual constructor(actual val nati awaitClose { listener.remove() } } + actual fun snapshots(listenOptions: SnapshotListenOptions) = callbackFlow { + val listener = + ios.addSnapshotListenerWithOptions(listenOptions.ios) { snapshot, error -> + snapshot?.let { trySend(snapshot) } + error?.let { close(error.toException()) } + } + awaitClose { listener.remove() } + } + val ios: NativeDocumentReferenceType by ::nativeValue actual val id: String @@ -40,8 +50,11 @@ internal actual class NativeDocumentReference actual constructor(actual val nati actual suspend fun setEncoded(encodedData: EncodedObject, setOptions: SetOptions) = await { when (setOptions) { is SetOptions.Merge -> ios.setData(encodedData.ios, true, it) + is SetOptions.Overwrite -> ios.setData(encodedData.ios, false, it) + is SetOptions.MergeFields -> ios.setData(encodedData.ios, setOptions.fields, it) + is SetOptions.MergeFieldPaths -> ios.setData( encodedData.ios, setOptions.encodedFieldPaths, @@ -68,7 +81,7 @@ internal actual class NativeDocumentReference actual constructor(actual val nati awaitClose { listener.remove() } } - override fun equals(other: Any?): Boolean = this === other || other is NativeDocumentReference && nativeValue == other.nativeValue + override fun equals(other: Any?): Boolean = this === other || (other is NativeDocumentReference && nativeValue == other.nativeValue) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() } diff --git a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt index a73ae5cfd..d5d84d659 100644 --- a/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt +++ b/firebase-firestore/src/appleMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt @@ -1,12 +1,15 @@ package dev.gitlive.firebase.firestore.internal import cocoapods.FirebaseFirestoreInternal.FIRFilter +import dev.gitlive.firebase.firestore.AggregateField import dev.gitlive.firebase.firestore.Direction import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.Filter +import dev.gitlive.firebase.firestore.NativeAggregateQuery import dev.gitlive.firebase.firestore.NativeDocumentSnapshot import dev.gitlive.firebase.firestore.NativeQuery import dev.gitlive.firebase.firestore.QuerySnapshot +import dev.gitlive.firebase.firestore.SnapshotListenOptions import dev.gitlive.firebase.firestore.Source import dev.gitlive.firebase.firestore.WhereConstraint import dev.gitlive.firebase.firestore.awaitResult @@ -18,6 +21,7 @@ import platform.Foundation.NSNull internal actual open class NativeQueryWrapper internal actual constructor(actual open val native: NativeQuery) { actual fun limit(limit: Number) = native.queryLimitedTo(limit.toLong()) + actual fun limitToLast(limit: Number) = native.queryLimitedToLast(limit.toLong()) actual suspend fun get(source: Source) = QuerySnapshot(awaitResult { native.getDocumentsWithSource(source.toIosSource(), it) }) @@ -38,11 +42,22 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual awaitClose { listener.remove() } } + actual fun snapshots(listenOptions: SnapshotListenOptions) = callbackFlow { + val listener = + native.addSnapshotListenerWithOptions(listenOptions.ios) { snapshot, error -> + snapshot?.let { trySend(QuerySnapshot(snapshot)) } + error?.let { close(error.toException()) } + } + awaitClose { listener.remove() } + } + actual fun where(filter: Filter) = native.queryWhereFilter(filter.toFIRFilter()) private fun Filter.toFIRFilter(): FIRFilter = when (this) { 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.value ?: NSNull.`null`()) is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereField(field, isNotEqualTo = constraint.value ?: NSNull.`null`()) @@ -55,6 +70,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual 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.value ?: NSNull.`null`()) is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isNotEqualTo = constraint.value ?: NSNull.`null`()) @@ -81,4 +97,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual actual fun endBefore(vararg fieldValues: Any) = native.queryEndingBeforeValues(fieldValues.asList()) actual fun endAt(document: NativeDocumentSnapshot) = native.queryEndingAtDocument(document) actual fun endAt(vararg fieldValues: Any) = native.queryEndingAtValues(fieldValues.asList()) + + actual fun count(): NativeAggregateQuery = native.count() + actual fun aggregate(aggregateField: AggregateField, vararg aggregateFields: AggregateField) = native.aggregate(listOf(aggregateField.ios, *aggregateFields.map { it.ios }.toTypedArray())) } diff --git a/firebase-firestore/src/appleTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt b/firebase-firestore/src/appleTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt index 25865dfcf..d2eaf49c9 100644 --- a/firebase-firestore/src/appleTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt +++ b/firebase-firestore/src/appleTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt @@ -21,6 +21,7 @@ import platform.Foundation.NSDefaultRunLoopMode import platform.Foundation.NSRunLoop import platform.Foundation.create import platform.Foundation.runMode +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -63,7 +64,7 @@ class ContextSwitchTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt new file mode 100644 index 000000000..63db34847 --- /dev/null +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt @@ -0,0 +1,31 @@ +package dev.gitlive.firebase.firestore + +public enum class MetadataChanges { + INCLUDE, + EXCLUDE, +} + +public enum class ListenSource { + CACHE, + DEFAULT, +} + +public expect class SnapshotListenOptions { + + public class Builder { + + public constructor() + + public var listenSource: ListenSource + public var metadataChanges: MetadataChanges + + public fun build(): SnapshotListenOptions + } + + public val listenSource: ListenSource + public val metadataChanges: MetadataChanges +} + +public fun snapshotListenOptions( + builder: SnapshotListenOptions.Builder.() -> Unit = {}, +): SnapshotListenOptions = SnapshotListenOptions.Builder().apply(builder).build() 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 5a62dcf27..14e09213d 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 @@ -10,6 +10,8 @@ import dev.gitlive.firebase.internal.EncodedObject import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.FirebaseApp import dev.gitlive.firebase.FirebaseException +import dev.gitlive.firebase.firestore.internal.NativeAggregateQuerySnapshotWrapper +import dev.gitlive.firebase.firestore.internal.NativeAggregateQueryWrapper import dev.gitlive.firebase.firestore.internal.NativeCollectionReferenceWrapper import dev.gitlive.firebase.firestore.internal.NativeDocumentReference import dev.gitlive.firebase.firestore.internal.NativeDocumentSnapshotWrapper @@ -247,8 +249,11 @@ public open class Query internal constructor(internal val nativeQuery: NativeQue internal open val native: NativeQuery = nativeQuery.native public fun limit(limit: Number): Query = Query(nativeQuery.limit(limit)) + public fun limitToLast(limit: Number): Query = Query(nativeQuery.limitToLast(limit)) public val snapshots: Flow = nativeQuery.snapshots - public fun snapshots(includeMetadataChanges: Boolean = false): Flow = nativeQuery.snapshots(includeMetadataChanges) + public fun snapshots(includeMetadataChanges: Boolean): Flow = nativeQuery.snapshots(includeMetadataChanges) + public fun snapshots(listenOptions: SnapshotListenOptions = snapshotListenOptions()): Flow = nativeQuery.snapshots(listenOptions) + public suspend fun get(source: Source = Source.DEFAULT): QuerySnapshot = nativeQuery.get(source) public fun where(builder: FilterBuilder.() -> Filter?): Query = builder(FilterBuilder())?.let { Query(nativeQuery.where(it)) } ?: this @@ -340,6 +345,12 @@ public open class Query internal constructor(internal val nativeQuery: NativeQue * @param builder closure for configuring the [FieldValuesDSL] */ public fun endAtFieldValues(builder: FieldValuesDSL.() -> Unit): Query = Query(nativeQuery.endAt(*FieldValuesDSL().apply(builder).fieldValues.toTypedArray())) + + public fun count(): AggregateQuery = AggregateQuery(nativeQuery.count()) + + public fun aggregate(aggregateField: AggregateField, vararg aggregateFields: AggregateField): AggregateQuery = AggregateQuery( + nativeQuery.aggregate(aggregateField, *aggregateFields), + ) } @Deprecated("Deprecated in favor of using a [FilterBuilder]", replaceWith = ReplaceWith("where { field equalTo equalTo }", "dev.gitlive.firebase.firestore")) @@ -394,6 +405,38 @@ public fun Query.where(path: FieldPath, inArray: List? = null, arrayContain ) } +internal expect class NativeAggregateQuery +internal expect class NativeAggregateQuerySnapshot + +public class AggregateQuery internal constructor(internal val nativeQuery: NativeAggregateQueryWrapper) { + + public companion object {} + + public val query: Query get() = Query(nativeQuery.query) + internal val native = nativeQuery.native + + internal constructor(native: NativeAggregateQuery) : this(NativeAggregateQueryWrapper(native)) + + public suspend fun get(source: AggregateSource = AggregateSource.SERVER): AggregateQuerySnapshot = AggregateQuerySnapshot(nativeQuery.get(source)) +} + +public class AggregateQuerySnapshot internal constructor(internal val nativeSnapshot: NativeAggregateQuerySnapshotWrapper) { + + public companion object {} + + public val query: AggregateQuery get() = AggregateQuery(nativeSnapshot.query) + internal val native = nativeSnapshot.native + public val count: Long get() = nativeSnapshot.count + + internal constructor(native: NativeAggregateQuerySnapshot) : this(NativeAggregateQuerySnapshotWrapper(native)) + + public operator fun get(aggregateField: AggregateField): Number? = nativeSnapshot[aggregateField] + public operator fun get(averageAggregateField: AggregateField.Average): Double? = nativeSnapshot[averageAggregateField] + public operator fun get(countAggregateField: AggregateField.Count): Long = nativeSnapshot[countAggregateField] + public fun getDouble(aggregateField: AggregateField): Double? = nativeSnapshot.getDouble(aggregateField) + public fun getLong(aggregateField: AggregateField): Long? = nativeSnapshot.getLong(aggregateField) +} + internal expect class NativeWriteBatch public data class WriteBatch internal constructor(internal val nativeWrapper: NativeWriteBatchWrapper) { @@ -524,7 +567,8 @@ public data class DocumentReference internal constructor(internal val native: Na val path: String get() = native.path val snapshots: Flow get() = native.snapshots.map(::DocumentSnapshot) val parent: CollectionReference get() = CollectionReference(native.parent) - public fun snapshots(includeMetadataChanges: Boolean = false): Flow = native.snapshots(includeMetadataChanges).map(::DocumentSnapshot) + public fun snapshots(includeMetadataChanges: Boolean): Flow = native.snapshots(includeMetadataChanges).map(::DocumentSnapshot) + public fun snapshots(listenOptions: SnapshotListenOptions = snapshotListenOptions()): Flow = native.snapshots(listenOptions).map(::DocumentSnapshot) public fun collection(collectionPath: String): CollectionReference = CollectionReference(native.collection(collectionPath)) public suspend fun get(source: Source = Source.DEFAULT): DocumentSnapshot = DocumentSnapshot(native.get(source)) @@ -827,3 +871,22 @@ public enum class Source { SERVER, DEFAULT, } + +public enum class AggregateSource { + SERVER, +} + +public expect sealed class AggregateField { + + public companion object { + public fun average(field: String): Average + public fun average(fieldPath: FieldPath): Average + public fun count(): Count + public fun sum(field: String): Sum + public fun sum(fieldPath: FieldPath): Sum + } + + public object Count : AggregateField + public class Average : AggregateField + public class Sum : AggregateField +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt new file mode 100644 index 000000000..802c1ebe5 --- /dev/null +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt @@ -0,0 +1,27 @@ +package dev.gitlive.firebase.firestore.internal + +import dev.gitlive.firebase.firestore.AggregateField +import dev.gitlive.firebase.firestore.AggregateSource +import dev.gitlive.firebase.firestore.NativeAggregateQuery +import dev.gitlive.firebase.firestore.NativeAggregateQuerySnapshot +import dev.gitlive.firebase.firestore.NativeQuery + +internal expect class NativeAggregateQueryWrapper(native: NativeAggregateQuery) { + + val native: NativeAggregateQuery + val query: NativeQuery + + suspend fun get(source: AggregateSource = AggregateSource.SERVER): NativeAggregateQuerySnapshot +} + +internal expect class NativeAggregateQuerySnapshotWrapper(native: NativeAggregateQuerySnapshot) { + val native: NativeAggregateQuerySnapshot + val query: NativeAggregateQuery + val count: Long + + operator fun get(aggregateField: AggregateField): Number? + operator fun get(averageAggregateField: AggregateField.Average): Double? + operator fun get(countAggregateField: AggregateField.Count): Long + fun getDouble(aggregateField: AggregateField): Double? + fun getLong(aggregateField: AggregateField): Long? +} 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 7cf376cf8..72e6f3539 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 @@ -3,7 +3,9 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.NativeCollectionReference import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot +import dev.gitlive.firebase.firestore.SnapshotListenOptions import dev.gitlive.firebase.firestore.Source +import dev.gitlive.firebase.firestore.snapshotListenOptions import dev.gitlive.firebase.internal.EncodedObject import kotlinx.coroutines.flow.Flow @@ -13,7 +15,8 @@ internal expect class NativeDocumentReference(nativeValue: NativeDocumentReferen val path: String val snapshots: Flow val parent: NativeCollectionReferenceWrapper - fun snapshots(includeMetadataChanges: Boolean = false): Flow + fun snapshots(includeMetadataChanges: Boolean): Flow + fun snapshots(listenOptions: SnapshotListenOptions = snapshotListenOptions()): Flow fun collection(collectionPath: String): NativeCollectionReference suspend fun get(source: Source = Source.DEFAULT): NativeDocumentSnapshot diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt index 5d1d1e67c..a271e06ff 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt @@ -1,12 +1,16 @@ package dev.gitlive.firebase.firestore.internal +import dev.gitlive.firebase.firestore.AggregateField import dev.gitlive.firebase.firestore.Direction import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.Filter +import dev.gitlive.firebase.firestore.NativeAggregateQuery import dev.gitlive.firebase.firestore.NativeDocumentSnapshot import dev.gitlive.firebase.firestore.NativeQuery import dev.gitlive.firebase.firestore.QuerySnapshot +import dev.gitlive.firebase.firestore.SnapshotListenOptions import dev.gitlive.firebase.firestore.Source +import dev.gitlive.firebase.firestore.snapshotListenOptions import kotlinx.coroutines.flow.Flow internal expect open class NativeQueryWrapper internal constructor(native: NativeQuery) { @@ -14,8 +18,10 @@ internal expect open class NativeQueryWrapper internal constructor(native: Nativ open val native: NativeQuery fun limit(limit: Number): NativeQuery + fun limitToLast(limit: Number): NativeQuery val snapshots: Flow - fun snapshots(includeMetadataChanges: Boolean = false): Flow + fun snapshots(includeMetadataChanges: Boolean): Flow + fun snapshots(listenOptions: SnapshotListenOptions = snapshotListenOptions()): Flow suspend fun get(source: Source = Source.DEFAULT): QuerySnapshot fun where(filter: Filter): NativeQuery @@ -32,4 +38,7 @@ internal expect open class NativeQueryWrapper internal constructor(native: Nativ fun endBefore(vararg fieldValues: Any): NativeQuery fun endAt(document: NativeDocumentSnapshot): NativeQuery fun endAt(vararg fieldValues: Any): NativeQuery + + fun count(): NativeAggregateQuery + fun aggregate(aggregateField: AggregateField, vararg aggregateFields: AggregateField): NativeAggregateQuery } diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/AggregateQueryTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/AggregateQueryTest.kt new file mode 100644 index 000000000..7d635cfae --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/AggregateQueryTest.kt @@ -0,0 +1,132 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.runTest +import kotlinx.serialization.KSerializer +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.time.Duration.Companion.milliseconds + +@IgnoreForAndroidUnitTest +open class AggregateQueryTest : 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 testCount() = runTestWithFirestoreData { + assertEquals( + 3L, + collection + .count().get().count, + ) + + assertEquals( + 3L, + collection.aggregate(AggregateField.count()).get()[AggregateField.count()], + ) + } + + @Test + fun testAverage() = runTestWithFirestoreData { + val averageTime = AggregateField.average(FirestoreTest::time.name) + val averageCount = AggregateField.average(FirestoreTest::count.name) + val averageProp = AggregateField.average(FirestoreTest::prop1.name) + val aggregate = collection.aggregate(averageTime, averageCount, averageProp).get() + assertEquals( + 2.0, + aggregate[averageCount], + ) + assertEquals( + 1.0 / 3.0, + aggregate[averageTime], + ) + assertNull(aggregate[averageProp]) + } + + @Test + fun testSum() = runTestWithFirestoreData { + val sumTime = AggregateField.sum(FirestoreTest::time.name) + val sumCount = AggregateField.sum(FirestoreTest::count.name) + val sumProp = AggregateField.sum(FirestoreTest::prop1.name) + val aggregate = collection.aggregate(sumTime, sumCount, sumProp).get() + assertEquals( + 6, + aggregate[sumCount]?.toInt(), + ) + assertEquals( + 1, + aggregate[sumTime]?.toInt(), + ) + assertEquals(0, aggregate[sumProp]?.toInt()) + } + + 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() + } +} 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 f87664321..be7d1a94b 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 @@ -1,6 +1,8 @@ package dev.gitlive.firebase.firestore import dev.gitlive.firebase.* +import kotlin.random.Random +import kotlin.random.nextInt import kotlin.test.* /** @@ -25,7 +27,7 @@ class FirestoreSourceTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", 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 7f79a7ebc..5d444a75b 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 @@ -18,6 +18,7 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -74,7 +75,7 @@ abstract class BaseFirebaseFirestoreTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt index f33eb606d..2459ab083 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/FieldValue.kt @@ -17,8 +17,10 @@ public actual class FieldValue internal actual constructor(internal actual val n require(nativeValue is NativeFieldValue) } override fun equals(other: Any?): Boolean = this === other || - other is FieldValue && - (nativeValue as NativeFieldValue).isEqual(other.nativeValue as NativeFieldValue) + ( + other is FieldValue && + (nativeValue as NativeFieldValue).isEqual(other.nativeValue as NativeFieldValue) + ) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = nativeValue.toString() diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt index 83248b38a..c658e8ef0 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/GeoPoint.kt @@ -12,7 +12,7 @@ public actual class GeoPoint internal actual constructor(internal actual val nat public actual val latitude: Double by nativeValue::latitude public actual val longitude: Double by nativeValue::longitude - override fun equals(other: Any?): Boolean = this === other || other is GeoPoint && nativeValue.isEqual(other.nativeValue) + override fun equals(other: Any?): Boolean = this === other || (other is GeoPoint && nativeValue.isEqual(other.nativeValue)) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = "GeoPoint[lat=$latitude,long=$longitude]" } diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt new file mode 100644 index 000000000..cac2ddeb9 --- /dev/null +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/SnapshotListenOptions.kt @@ -0,0 +1,30 @@ +package dev.gitlive.firebase.firestore + +import kotlin.js.Json +import kotlin.js.json + +public actual data class SnapshotListenOptions( + public actual val listenSource: ListenSource, + public actual val metadataChanges: MetadataChanges, +) { + public actual class Builder actual constructor() { + public actual var listenSource: ListenSource = ListenSource.DEFAULT + public actual var metadataChanges: MetadataChanges = MetadataChanges.EXCLUDE + + public actual fun build(): SnapshotListenOptions = SnapshotListenOptions( + listenSource = listenSource, + metadataChanges = metadataChanges, + ) + } + + public val js: Json = json( + "includeMetadataChanges" to when (metadataChanges) { + MetadataChanges.INCLUDE -> true + MetadataChanges.EXCLUDE -> false + }, + "source" to when (listenSource) { + ListenSource.DEFAULT -> "default" + ListenSource.CACHE -> "cache" + }, + ) +} diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt index 28d7077e8..59c165980 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt @@ -19,7 +19,7 @@ public actual class Timestamp internal actual constructor( public actual val seconds: Long = nativeValue.seconds.toLong() public actual val nanoseconds: Int = nativeValue.nanoseconds.toInt() - override fun equals(other: Any?): Boolean = this === other || other is Timestamp && nativeValue.isEqual(other.nativeValue) + override fun equals(other: Any?): Boolean = this === other || (other is Timestamp && nativeValue.isEqual(other.nativeValue)) override fun hashCode(): Int = nativeValue.toMillis().hashCode() override fun toString(): String = nativeValue.toString() diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt index a1e313dd6..ebd2e5029 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/_encoders.kt @@ -7,5 +7,6 @@ internal actual fun isSpecialValue(value: Any): Boolean = when (value) { is NativeTimestamp, is NativeDocumentReferenceType, -> true + else -> false } diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/externals/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/externals/firestore.kt index 00d8de655..182302b19 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/externals/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/externals/firestore.kt @@ -88,6 +88,7 @@ public external fun increment(n: Int): FieldValue public external fun initializeFirestore(app: FirebaseApp, settings: dynamic = definedExternally, databaseId: String? = definedExternally): Firestore public external fun limit(limit: Number): QueryConstraint +public external fun limitToLast(limit: Number): QueryConstraint public external fun onSnapshot( reference: DocumentReference, @@ -225,6 +226,27 @@ public external interface QuerySnapshot { public fun docChanges(): Array } +public external interface AggregateField + +public external fun count(): AggregateField +public external fun sum(field: String): AggregateField +public external fun average(field: String): AggregateField + +public external fun getAggregateFromServer( + query: Query, + aggregateSpec: Json, +): Promise + +public external fun getCount( + query: Query, +): Promise + +public external interface AggregateQuerySnapshot { + public val query: Query + + public fun data(): Json +} + public external interface SnapshotMetadata { public val hasPendingWrites: Boolean public val fromCache: Boolean diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index fff415f68..8f736e8b2 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -19,6 +19,8 @@ import dev.gitlive.firebase.firestore.internal.NativeFirebaseFirestoreWrapper import dev.gitlive.firebase.js import kotlin.js.Json import kotlin.js.json +import dev.gitlive.firebase.firestore.externals.AggregateField as JsAggregateField +import dev.gitlive.firebase.firestore.externals.AggregateQuerySnapshot as JsAggregateQuerySnapshot import dev.gitlive.firebase.firestore.externals.Firestore as JsFirestore import dev.gitlive.firebase.firestore.externals.CollectionReference as JsCollectionReference import dev.gitlive.firebase.firestore.externals.DocumentChange as JsDocumentChange @@ -30,6 +32,9 @@ import dev.gitlive.firebase.firestore.externals.QuerySnapshot as JsQuerySnapshot import dev.gitlive.firebase.firestore.externals.SnapshotMetadata as JsSnapshotMetadata import dev.gitlive.firebase.firestore.externals.Transaction as JsTransaction import dev.gitlive.firebase.firestore.externals.WriteBatch as JsWriteBatch +import dev.gitlive.firebase.firestore.externals.average as jsAverage +import dev.gitlive.firebase.firestore.externals.count as jsCount +import dev.gitlive.firebase.firestore.externals.sum as jsSum import dev.gitlive.firebase.firestore.externals.documentId as jsDocumentId public actual val Firebase.firestore: FirebaseFirestore get() = @@ -87,6 +92,7 @@ public actual data class FirebaseFirestoreSettings( "cacheSizeBytes" to cacheSettings.sizeBytes, ).asDynamic() as PersistentCacheSettings, ) + is LocalCacheSettings.Memory -> { val garbageCollectorSettings = when (val garbageCollectorSettings = cacheSettings.garbaseCollectorSettings) { is MemoryGarbageCollectorSettings.Eager -> memoryEagerGarbageCollector() @@ -144,6 +150,12 @@ public actual val FirebaseFirestoreException.code: FirestoreExceptionCode get() public val QuerySnapshot.js: JsQuerySnapshot get() = js +internal actual data class NativeAggregateQuery(val query: JsQuery, val aggregateFields: List) +internal actual data class NativeAggregateQuerySnapshot(val js: JsAggregateQuerySnapshot) + +public operator fun AggregateQuerySnapshot.Companion.invoke(android: JsAggregateQuerySnapshot): AggregateQuerySnapshot = AggregateQuerySnapshot(android) +public val AggregateQuerySnapshot.js: JsAggregateQuerySnapshot get() = native.js + public actual class QuerySnapshot(internal val js: JsQuerySnapshot) { public actual val documents: List get() = js.docs.map { DocumentSnapshot(NativeDocumentSnapshotWrapper(it)) } @@ -194,6 +206,8 @@ public actual class FieldPath private constructor(internal val js: JsFieldPath) override fun equals(other: Any?): Boolean = other is FieldPath && js.isEqual(other.js) override fun hashCode(): Int = js.hashCode() override fun toString(): String = js.toString() + + internal val pathString: String get() = js.asDynamic()["_internalPath"].toString() } public actual typealias EncodedFieldPath = JsFieldPath @@ -229,6 +243,39 @@ public actual enum class ChangeType(internal val jsString: String) { REMOVED("removed"), } +public actual sealed class AggregateField { + + public abstract val js: JsAggregateField<*> + internal abstract val alias: String + + public actual companion object { + public actual fun average(field: String): Average = Average(jsAverage(field), "averageOf$field") + public actual fun average(fieldPath: FieldPath): Average = fieldPath.pathString.let { + println("Path is $it") + Average(jsAverage(it), "averageOf$it") + } + public actual fun count(): Count = Count + public actual fun sum(field: String): Sum = Sum(jsSum(field), "sumOf$field") + public actual fun sum(fieldPath: FieldPath): Sum = fieldPath.pathString.let { + println("Path is $it") + Sum(jsSum(it), "sumOf$it") + } + } + + public actual data object Count : AggregateField() { + override val js: JsAggregateField get() = jsCount() + override val alias: String = "countOfDocs" + } + public actual data class Average internal constructor( + override val js: JsAggregateField, + override val alias: String, + ) : AggregateField() + public actual data class Sum internal constructor( + override val js: JsAggregateField, + override val alias: String, + ) : AggregateField() +} + internal inline fun T.rethrow(function: T.() -> R): R = dev.gitlive.firebase.firestore.rethrow { function() } internal inline fun rethrow(function: () -> R): R { @@ -247,21 +294,37 @@ internal fun errorToException(e: dynamic) = (e?.code ?: e?.message ?: "") .let { when { "cancelled" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.CANCELLED) + "invalid-argument" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.INVALID_ARGUMENT) + "deadline-exceeded" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.DEADLINE_EXCEEDED) + "not-found" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.NOT_FOUND) + "already-exists" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.ALREADY_EXISTS) + "permission-denied" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.PERMISSION_DENIED) + "resource-exhausted" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.RESOURCE_EXHAUSTED) + "failed-precondition" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.FAILED_PRECONDITION) + "aborted" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.ABORTED) + "out-of-range" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.OUT_OF_RANGE) + "unimplemented" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.UNIMPLEMENTED) + "internal" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.INTERNAL) + "unavailable" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.UNAVAILABLE) + "data-loss" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.DATA_LOSS) + "unauthenticated" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.UNAUTHENTICATED) + "unknown" in it -> FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.UNKNOWN) + else -> { println("Unknown error code in ${JSON.stringify(e)}") FirebaseFirestoreException(e.unsafeCast(), FirestoreExceptionCode.UNKNOWN) diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt new file mode 100644 index 000000000..2e5a18102 --- /dev/null +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeAggregateQueryWrapper.kt @@ -0,0 +1,69 @@ +package dev.gitlive.firebase.firestore.internal + +import dev.gitlive.firebase.firestore.AggregateField +import dev.gitlive.firebase.firestore.AggregateSource +import dev.gitlive.firebase.firestore.FieldPath +import dev.gitlive.firebase.firestore.NativeAggregateQuery +import dev.gitlive.firebase.firestore.NativeAggregateQuerySnapshot +import dev.gitlive.firebase.firestore.NativeQuery +import dev.gitlive.firebase.firestore.externals.getAggregateFromServer +import dev.gitlive.firebase.firestore.externals.getCount +import dev.gitlive.firebase.firestore.wrapped +import kotlinx.coroutines.await +import kotlin.js.json + +internal actual class NativeAggregateQueryWrapper actual constructor(actual val native: NativeAggregateQuery) { + + actual val query: NativeQuery get() = native.query.wrapped + + actual suspend fun get(source: AggregateSource): NativeAggregateQuerySnapshot = if (native.aggregateFields.size == 1 && native.aggregateFields.first() == AggregateField.Count.js) { + getCount(query.js) + } else { + getAggregateFromServer( + query.js, + json( + *native.aggregateFields.map { field -> + field.alias to field.js + }.toTypedArray(), + ), + ) + }.await().let(::NativeAggregateQuerySnapshot) +} + +internal actual class NativeAggregateQuerySnapshotWrapper actual constructor(actual val native: NativeAggregateQuerySnapshot) { + + actual val query: NativeAggregateQuery get() = NativeAggregateQuery(native.js.query, aggregateFields()) + actual val count: Long get() = (native.js.data()[AggregateField.Count.alias] as Int).toLong() + + actual operator fun get(aggregateField: AggregateField): Number? = native.js.data()[aggregateField.alias] as? Number + actual operator fun get(averageAggregateField: AggregateField.Average): Double? = getDouble(averageAggregateField) + actual operator fun get(countAggregateField: AggregateField.Count): Long = getLong(countAggregateField)!! + actual fun getDouble(aggregateField: AggregateField): Double? = get(aggregateField)?.toDouble() + actual fun getLong(aggregateField: AggregateField): Long? = get(aggregateField)?.toLong() + + private fun aggregateFields(): List { + // JS Does not have the AggregateQuery as a separate object, but we can extract it from data + val dataString = JSON.stringify(native.js.data()) + val elements = dataString.substring(1, dataString.length - 1).split(",") + return elements.mapNotNull { element -> + val alias = element.drop(1).takeWhile { it != '"' } + when { + alias == AggregateField.Count.alias -> AggregateField.Count + + alias.startsWith("sumOf") -> AggregateField.sum( + FieldPath( + *alias.removePrefix("sumOf").split(".").toTypedArray(), + ), + ) + + alias.startsWith("averageOf") -> AggregateField.average( + FieldPath( + *alias.removePrefix("averageOf").split(".").toTypedArray(), + ), + ) + + else -> null + } + } + } +} 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 681f8b77f..c8cc7d0a0 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 @@ -3,6 +3,7 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.NativeCollectionReference import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot +import dev.gitlive.firebase.firestore.SnapshotListenOptions import dev.gitlive.firebase.firestore.Source import dev.gitlive.firebase.firestore.errorToException import dev.gitlive.firebase.firestore.externals.deleteDoc @@ -62,6 +63,16 @@ internal actual class NativeDocumentReference actual constructor(actual val nati awaitClose { unsubscribe() } } + actual fun snapshots(listenOptions: SnapshotListenOptions) = callbackFlow { + val unsubscribe = onSnapshot( + js, + listenOptions.js, + { trySend(NativeDocumentSnapshot(it)) }, + { close(errorToException(it)) }, + ) + awaitClose { unsubscribe() } + } + actual suspend fun setEncoded(encodedData: EncodedObject, setOptions: SetOptions) = rethrow { setDoc(js, encodedData.js, setOptions.js).await() } @@ -91,11 +102,13 @@ internal actual class NativeDocumentReference actual constructor(actual val nati actual suspend fun delete() = rethrow { deleteDoc(js).await() } override fun equals(other: Any?): Boolean = this === other || - other is NativeDocumentReference && - refEqual( - nativeValue, - other.nativeValue, - ) + ( + other is NativeDocumentReference && + refEqual( + nativeValue, + other.nativeValue, + ) + ) override fun hashCode(): Int = nativeValue.hashCode() override fun toString(): String = "DocumentReference(path=$path)" } 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 04327dfd1..f64292293 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 @@ -1,11 +1,14 @@ package dev.gitlive.firebase.firestore.internal +import dev.gitlive.firebase.firestore.AggregateField import dev.gitlive.firebase.firestore.Direction import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.Filter +import dev.gitlive.firebase.firestore.NativeAggregateQuery import dev.gitlive.firebase.firestore.NativeDocumentSnapshot import dev.gitlive.firebase.firestore.NativeQuery import dev.gitlive.firebase.firestore.QuerySnapshot +import dev.gitlive.firebase.firestore.SnapshotListenOptions import dev.gitlive.firebase.firestore.Source import dev.gitlive.firebase.firestore.WhereConstraint import dev.gitlive.firebase.firestore.errorToException @@ -23,6 +26,7 @@ import dev.gitlive.firebase.firestore.wrapped import kotlinx.coroutines.await import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow +import kotlin.collections.listOf import kotlin.js.json internal actual open class NativeQueryWrapper internal actual constructor(actual open val native: NativeQuery) { @@ -38,11 +42,18 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual dev.gitlive.firebase.firestore.externals.limit(limit), ).wrapped + actual fun limitToLast(limit: Number) = query( + js, + dev.gitlive.firebase.firestore.externals.limitToLast(limit), + ).wrapped + actual fun where(filter: Filter) = query(js, filter.toQueryConstraint()).wrapped private fun Filter.toQueryConstraint(): QueryConstraint = when (this) { is Filter.And -> and(*filters.map { it.toQueryConstraint() }.toTypedArray()) + is Filter.Or -> or(*filters.map { it.toQueryConstraint() }.toTypedArray()) + is Filter.Field -> { val value = when (constraint) { is WhereConstraint.ForNullableObject -> constraint.value @@ -51,6 +62,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } dev.gitlive.firebase.firestore.externals.where(field, constraint.filterOp, value) } + is Filter.Path -> { val value = when (constraint) { is WhereConstraint.ForNullableObject -> constraint.value @@ -138,6 +150,12 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual ).wrapped } + actual fun count(): NativeAggregateQuery = NativeAggregateQuery(native.js, listOf(AggregateField.Count)) + actual fun aggregate( + aggregateField: AggregateField, + vararg aggregateFields: AggregateField, + ): NativeAggregateQuery = NativeAggregateQuery(native.js, listOf(aggregateField, *aggregateFields)) + actual val snapshots get() = callbackFlow { val unsubscribe = rethrow { onSnapshot( @@ -160,6 +178,18 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual } awaitClose { rethrow { unsubscribe() } } } + + actual fun snapshots(listenOptions: SnapshotListenOptions) = callbackFlow { + val unsubscribe = rethrow { + onSnapshot( + js, + listenOptions.js, + { trySend(QuerySnapshot(it)) }, + { close(errorToException(it)) }, + ) + } + awaitClose { rethrow { unsubscribe() } } + } } private fun Query.get(source: Source) = when (source) { diff --git a/firebase-firestore/src/jsTest/kotlin/dev/gitlive/firebase/firestore/FieldPathTest.kt b/firebase-firestore/src/jsTest/kotlin/dev/gitlive/firebase/firestore/FieldPathTest.kt new file mode 100644 index 000000000..30f8f6240 --- /dev/null +++ b/firebase-firestore/src/jsTest/kotlin/dev/gitlive/firebase/firestore/FieldPathTest.kt @@ -0,0 +1,13 @@ +package dev.gitlive.firebase.firestore + +import kotlin.test.Test +import kotlin.test.assertEquals + +class FieldPathTest { + + @Test + fun testFieldPathString() { + val fieldPath = FieldPath("field1", "field2", "field3") + assertEquals("field1.field2.field3", fieldPath.pathString) + } +} diff --git a/firebase-functions/src/jsMain/kotlin/dev/gitlive/firebase/functions/functions.kt b/firebase-functions/src/jsMain/kotlin/dev/gitlive/firebase/functions/functions.kt index 765869039..6312766ea 100644 --- a/firebase-functions/src/jsMain/kotlin/dev/gitlive/firebase/functions/functions.kt +++ b/firebase-functions/src/jsMain/kotlin/dev/gitlive/firebase/functions/functions.kt @@ -114,21 +114,37 @@ internal fun errorToException(e: dynamic): FirebaseFunctionsException = (e?.code .let { when { "cancelled" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.CANCELLED, e.details) + "invalid-argument" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.INVALID_ARGUMENT, e.details) + "deadline-exceeded" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.DEADLINE_EXCEEDED, e.details) + "not-found" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.NOT_FOUND, e.details) + "already-exists" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.ALREADY_EXISTS, e.details) + "permission-denied" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.PERMISSION_DENIED, e.details) + "resource-exhausted" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.RESOURCE_EXHAUSTED, e.details) + "failed-precondition" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.FAILED_PRECONDITION, e.details) + "aborted" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.ABORTED, e.details) + "out-of-range" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.OUT_OF_RANGE, e.details) + "unimplemented" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.UNIMPLEMENTED, e.details) + "internal" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.INTERNAL, e.details) + "unavailable" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.UNAVAILABLE, e.details) + "data-loss" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.DATA_LOSS, e.details) + "unauthenticated" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.UNAUTHENTICATED, e.details) + "unknown" in it -> FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.UNKNOWN, e.details) + else -> { println("Unknown error code in ${JSON.stringify(e)}") FirebaseFunctionsException(e.unsafeCast(), FunctionsExceptionCode.UNKNOWN, e.details) diff --git a/firebase-messaging/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/messaging/messaging.kt b/firebase-messaging/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/messaging/messaging.kt index eef7d337e..4602b0fd1 100644 --- a/firebase-messaging/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/messaging/messaging.kt +++ b/firebase-messaging/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/messaging/messaging.kt @@ -5,6 +5,7 @@ import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.FirebaseOptions import dev.gitlive.firebase.apps import dev.gitlive.firebase.initialize +import kotlin.random.Random import kotlin.test.BeforeTest class AndroidInstrumentedFirebaseMessagingTest : FirebaseMessagingTest() { @@ -16,7 +17,7 @@ class AndroidInstrumentedFirebaseMessagingTest : FirebaseMessagingTest() { Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-perf/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt b/firebase-perf/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt index 5db4a766f..a1b13db4e 100644 --- a/firebase-perf/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt +++ b/firebase-perf/src/androidInstrumentedTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt @@ -10,6 +10,7 @@ import dev.gitlive.firebase.perf.performance import dev.gitlive.firebase.runBlockingTest import kotlinx.coroutines.delay import kotlinx.coroutines.test.runTest +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -25,7 +26,7 @@ class AndroidTraceTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-perf/src/commonTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt b/firebase-perf/src/commonTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt index e3298744d..a88f4ab83 100644 --- a/firebase-perf/src/commonTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt +++ b/firebase-perf/src/commonTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt @@ -11,6 +11,7 @@ import dev.gitlive.firebase.perf.performance import dev.gitlive.firebase.runBlockingTest import dev.gitlive.firebase.runTest import kotlinx.coroutines.delay +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -27,7 +28,7 @@ class TraceTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-perf/src/commonTest/kotlin/dev/gitlive/firebase/perf/performance.kt b/firebase-perf/src/commonTest/kotlin/dev/gitlive/firebase/perf/performance.kt index 69d44b621..e3d70cd23 100644 --- a/firebase-perf/src/commonTest/kotlin/dev/gitlive/firebase/perf/performance.kt +++ b/firebase-perf/src/commonTest/kotlin/dev/gitlive/firebase/perf/performance.kt @@ -11,6 +11,7 @@ import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runBlockingTest import dev.gitlive.firebase.runTest import kotlinx.coroutines.delay +import kotlin.random.Random import kotlin.test.* import kotlin.time.Duration.Companion.seconds @@ -27,7 +28,7 @@ class FirebasePerformanceTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-perf/src/jsTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt b/firebase-perf/src/jsTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt index 686878e68..41e37d430 100644 --- a/firebase-perf/src/jsTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt +++ b/firebase-perf/src/jsTest/kotlin/dev/gitlive/firebase/perf/metrics/Trace.kt @@ -23,7 +23,7 @@ class JsTraceTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-perf/src/jsTest/kotlin/dev/gitlive/firebase/perf/performance.kt b/firebase-perf/src/jsTest/kotlin/dev/gitlive/firebase/perf/performance.kt index 821b92f54..66e941473 100644 --- a/firebase-perf/src/jsTest/kotlin/dev/gitlive/firebase/perf/performance.kt +++ b/firebase-perf/src/jsTest/kotlin/dev/gitlive/firebase/perf/performance.kt @@ -25,7 +25,7 @@ class JsPerformanceTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/firebase-storage/src/appleMain/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/appleMain/kotlin/dev/gitlive/firebase/storage/storage.kt index 46dcbe97e..f75e24d1e 100644 --- a/firebase-storage/src/appleMain/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/appleMain/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -134,6 +134,7 @@ public actual class StorageReference(internal val ios: FIRStorageReference) { when (it!!.error()!!.code) { /*FIRStorageErrorCodeCancelled = */ -13040L -> cancel(it.error()!!.localizedDescription) + else -> close(FirebaseStorageException(it.error().toString())) } } diff --git a/firebase-storage/src/commonTest/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/commonTest/kotlin/dev/gitlive/firebase/storage/storage.kt index 363dd83f5..3dbc4d8b3 100644 --- a/firebase-storage/src/commonTest/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/commonTest/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -10,6 +10,7 @@ import dev.gitlive.firebase.apps import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runBlockingTest import dev.gitlive.firebase.runTest +import kotlin.random.Random import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -31,7 +32,7 @@ class FirebaseStorageTest { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + applicationId = "1:${Random.nextInt()}:ios:dd1f6688bad7af768c841a", apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", storageBucket = "fir-kotlin-sdk.appspot.com", diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2a9334382..d0c959e0a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,28 +1,40 @@ [versions] -agp = "8.12.0" -androidx-test-core = "1.7.0" -androidx-test-junit = "1.3.0" -androidx-test-runner = "1.7.0" -ben-manes-versions = "0.52.0" -firebase-bom = "33.15.0" -gitlive-firebase-java-sdk = "0.6.3" -gson = "2.13.1" -junit = "4.13.2" -kotlin = "2.2.20" +#Kotlin +kotlin = "2.2.21" kotlinx-coroutines = "1.10.2" kotlinx-serialization = "1.9.0" kotlinx-binarycompatibilityvalidator = "0.18.1" kotlinx-datetime = "0.7.1" -kotlinter = "5.2.0" settings-api = "2.2" settings-language = "2.2" -firebase-cocoapods = "11.8.0" -ios-deploymentTarget = "13.0" -tvos-deploymentTarget = "13.0" + +#Android +agp = "8.13.1" +androidx-test-core = "1.7.0" +androidx-test-junit = "1.3.0" +androidx-test-runner = "1.7.0" + +# Apple +ios-deploymentTarget = "15.0" +tvos-deploymentTarget = "15.0" macos-deploymentTarget = "10.15" + +# Firebase +firebase-bom = "34.6.0" +gitlive-firebase-java-sdk = "0.6.4" +firebase-cocoapods = "12.6.0" +firebase-npm = "12.6.0" + +# Testing +gson = "2.13.2" +junit = "4.13.2" test-logger-plugin = "4.0.0" -dokka = "2.0.0" -publish = "0.34.0" + +# Meta +ben-manes-versions = "0.53.0" +kotlinter = "5.3.0" +dokka = "2.1.0" +publish = "0.35.0" [libraries] android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" }