diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 273eef84e..828d7d732 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -4,8 +4,10 @@ name: Publish on: - release: - types: [ prereleased, released ] + ## do not upstream ## + push: + branches: [ master, snapshot ] + ## ############### ## env: sonatypeUsername: ${{ secrets.SONATYPEUSERNAME }} @@ -23,6 +25,11 @@ jobs: with: distribution: 'zulu' java-version: '17' + ## do no upstream ## + - name: replace -SNAPSHOT with -run_number-SNAPSHOT + run: sed -i '' 's/-SNAPSHOT/-${{github.run_number}}-SNAPSHOT/g' gradle.properties + shell: bash + ## ############## ## - uses: gradle/actions/setup-gradle@v4 - name: Setup versions run: ./gradlew :updateVersions diff --git a/README.md b/README.md index f92376210..cb7b1175e 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,17 @@ The following libraries are available for the various Firebase products. | Service or Product | Gradle Dependency | API Coverage | |---------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [Analytics](https://firebase.google.com/docs/analytics) | [`dev.gitlive:firebase-analytics:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-analytics/2.0.0/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt) | -| [Authentication](https://firebase.google.com/docs/auth) | [`dev.gitlive:firebase-auth:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-auth/2.0.0/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt) | -| [Realtime Database](https://firebase.google.com/docs/database) | [`dev.gitlive:firebase-database:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-database/2.0.0/pom) | [![70%](https://img.shields.io/badge/-70%25-orange?style=flat-square)](/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt) | -| [Cloud Firestore](https://firebase.google.com/docs/firestore) | [`dev.gitlive:firebase-firestore:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-firestore/2.0.0/pom) | [![60%](https://img.shields.io/badge/-60%25-orange?style=flat-square)](/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt) | -| [Cloud Functions](https://firebase.google.com/docs/functions) | [`dev.gitlive:firebase-functions:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-functions/2.0.0/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-functions/src/commonMain/kotlin/dev/gitlive/firebase/functions/functions.kt) | -| [Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) | [`dev.gitlive:firebase-messaging:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-messaging/2.0.0/pom) | [![1%](https://img.shields.io/badge/-10%25-orange?style=flat-square)](/firebase-messaging/src/commonMain/kotlin/dev/gitlive/firebase/messaging/messaging.kt) | -| [Cloud Storage](https://firebase.google.com/docs/storage) | [`dev.gitlive:firebase-storage:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-storage/2.0.0/pom) | [![40%](https://img.shields.io/badge/-40%25-orange?style=flat-square)](/firebase-storage/src/commonMain/kotlin/dev/gitlive/firebase/storage/storage.kt) | -| [Installations](https://firebase.google.com/docs/projects/manage-installations) | [`dev.gitlive:firebase-installations:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-installations/2.0.0/pom) | [![90%](https://img.shields.io/badge/-90%25-green?style=flat-square)](/firebase-installations/src/commonMain/kotlin/dev/gitlive/firebase/installations/installations.kt) | -| [Remote Config](https://firebase.google.com/docs/remote-config) | [`dev.gitlive:firebase-config:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-config/2.0.0/pom) | [![20%](https://img.shields.io/badge/-20%25-orange?style=flat-square)](/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt) | -| [Performance](https://firebase.google.com/docs/perf-mon) | [`dev.gitlive:firebase-perf:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-perf/2.0.0/pom) | [![1%](https://img.shields.io/badge/-10%25-orange?style=flat-square)](/firebase-perf/src/commonMain/kotlin/dev/gitlive/firebase/perf/performance.kt) | -| [Crashlytics](https://firebase.google.com/docs/crashlytics) | [`dev.gitlive:firebase-crashlytics:2.0.0`](https://search.maven.org/artifact/dev.gitlive/firebase-crashlytics/2.0.0/pom) | [![80%](https://img.shields.io/badge/-10%25-orange?style=flat-square)](/firebase-crashlytics/src/commonMain/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt) | +| [Analytics](https://firebase.google.com/docs/analytics) | [`dev.gitlive:firebase-analytics:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-analytics/2.1.0/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt) | +| [Authentication](https://firebase.google.com/docs/auth) | [`dev.gitlive:firebase-auth:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-auth/2.1.0/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt) | +| [Realtime Database](https://firebase.google.com/docs/database) | [`dev.gitlive:firebase-database:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-database/2.1.0/pom) | [![70%](https://img.shields.io/badge/-70%25-orange?style=flat-square)](/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt) | +| [Cloud Firestore](https://firebase.google.com/docs/firestore) | [`dev.gitlive:firebase-firestore:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-firestore/2.1.0/pom) | [![60%](https://img.shields.io/badge/-60%25-orange?style=flat-square)](/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt) | +| [Cloud Functions](https://firebase.google.com/docs/functions) | [`dev.gitlive:firebase-functions:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-functions/2.1.0/pom) | [![80%](https://img.shields.io/badge/-80%25-green?style=flat-square)](/firebase-functions/src/commonMain/kotlin/dev/gitlive/firebase/functions/functions.kt) | +| [Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) | [`dev.gitlive:firebase-messaging:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-messaging/2.1.0/pom) | [![1%](https://img.shields.io/badge/-10%25-orange?style=flat-square)](/firebase-messaging/src/commonMain/kotlin/dev/gitlive/firebase/messaging/messaging.kt) | +| [Cloud Storage](https://firebase.google.com/docs/storage) | [`dev.gitlive:firebase-storage:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-storage/2.1.0/pom) | [![40%](https://img.shields.io/badge/-40%25-orange?style=flat-square)](/firebase-storage/src/commonMain/kotlin/dev/gitlive/firebase/storage/storage.kt) | +| [Installations](https://firebase.google.com/docs/projects/manage-installations) | [`dev.gitlive:firebase-installations:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-installations/2.1.0/pom) | [![90%](https://img.shields.io/badge/-90%25-green?style=flat-square)](/firebase-installations/src/commonMain/kotlin/dev/gitlive/firebase/installations/installations.kt) | +| [Remote Config](https://firebase.google.com/docs/remote-config) | [`dev.gitlive:firebase-config:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-config/2.1.0/pom) | [![20%](https://img.shields.io/badge/-20%25-orange?style=flat-square)](/firebase-config/src/commonMain/kotlin/dev/gitlive/firebase/remoteconfig/FirebaseRemoteConfig.kt) | +| [Performance](https://firebase.google.com/docs/perf-mon) | [`dev.gitlive:firebase-perf:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-perf/2.1.0/pom) | [![1%](https://img.shields.io/badge/-10%25-orange?style=flat-square)](/firebase-perf/src/commonMain/kotlin/dev/gitlive/firebase/perf/performance.kt) | +| [Crashlytics](https://firebase.google.com/docs/crashlytics) | [`dev.gitlive:firebase-crashlytics:2.1.0`](https://search.maven.org/artifact/dev.gitlive/firebase-crashlytics/2.1.0/pom) | [![80%](https://img.shields.io/badge/-10%25-orange?style=flat-square)](/firebase-crashlytics/src/commonMain/kotlin/dev/gitlive/firebase/crashlytics/crashlytics.kt) | Is the Firebase library or API you need missing? [Create an issue](https://github.com/GitLiveApp/firebase-kotlin-sdk/issues/new?labels=API+coverage&template=increase-api-coverage.md&title=Add+%5Bclass+name%5D.%5Bfunction+name%5D+to+%5Blibrary+name%5D+for+%5Bplatform+names%5D) to request additional API coverage or be awesome and [submit a PR](https://github.com/GitLiveApp/firebase-kotlin-sdk/fork) @@ -250,6 +250,15 @@ citiesRef.where { } ``` +Similar methods exist for `update` methods in the Firestore module: + +```kotlin +documentRef.update { + "field" to "value" + "otherField".to(IntAsStringSerializer(), 1) +} +``` +

Operator overloading

In cases where it makes sense, such as Firebase Functions HTTPS Callable, operator overloading is used: @@ -278,9 +287,13 @@ These properties are only accessible from the equivalent target's source set. Fo .build() ``` +### Running on Android + +On android, some modules (`config`) require you to enable [Core library desugaring](https://developer.android.com/studio/write/java8-support#library-desugaring) if you have a *minSDK lower than API 26*. + ### Running on iOS -On iOS the official [Firebase iOS SDK](https://github.com/firebase/firebase-ios-sdk) in not linked as a transtive dependency. Therefore, any project using this SDK needs to link the actual Firestore SDK as well. This can be done through your preferred installation method (Cocoapods/SPM). +On iOS the official [Firebase iOS SDK](https://github.com/firebase/firebase-ios-sdk) in not linked as a transitive dependency. Therefore, any project using this SDK needs to link the actual Firestore SDK as well. This can be done through your preferred installation method (Cocoapods/SPM). Similarly, tests require linking as well. Make sure to add the required frameworks to the search path of your test targets. This can be done by specifying a `cocoapods` block in your `build.gradle`: ```kotlin diff --git a/build.gradle.kts b/build.gradle.kts index ac5150679..7f92541d9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,7 +70,9 @@ tasks.withType().configureEach { subprojects { - group = "dev.gitlive" + //// do not upstream //// + group = "com.splendo.firebase" + //// /////////////// //// val nonDocumentationList = listOf("test-utils", "firebase-common", "firebase-common-internal") val skipDocumentation = nonDocumentationList.contains(project.name) @@ -196,7 +198,10 @@ subprojects { repositories { maven { - url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2") + //// do not upstream //// + this.name = "snapshot" + url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") + //// /////////////// //// credentials { username = project.findProperty("sonatypeUsername") as String? ?: System.getenv("sonatypeUsername") diff --git a/firebase-analytics/package.json b/firebase-analytics/package.json index da4f8da37..4db9c7135 100644 --- a/firebase-analytics/package.json +++ b/firebase-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-analytics", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-analytics.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.6.10", "kotlinx-coroutines-core": "1.6.1-native-mt" diff --git a/firebase-app/package.json b/firebase-app/package.json index 9fceec0af..575fc918a 100644 --- a/firebase-app/package.json +++ b/firebase-app/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-app", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-app.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-common": "2.0.0", + "@gitlive/firebase-common": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4" diff --git a/firebase-auth/api/android/firebase-auth.api b/firebase-auth/api/android/firebase-auth.api index a5ac17791..d997bf223 100644 --- a/firebase-auth/api/android/firebase-auth.api +++ b/firebase-auth/api/android/firebase-auth.api @@ -51,6 +51,14 @@ public final class dev/gitlive/firebase/auth/ActionCodeSettings { public fun toString ()Ljava/lang/String; } +public final class dev/gitlive/firebase/auth/AdditionalUserInfo { + public fun (Lcom/google/firebase/auth/AdditionalUserInfo;)V + public final fun getProfile ()Ljava/util/Map; + public final fun getProviderId ()Ljava/lang/String; + public final fun getUsername ()Ljava/lang/String; + public final fun isNewUser ()Z +} + public final class dev/gitlive/firebase/auth/AndroidPackageName { public fun (Ljava/lang/String;ZLjava/lang/String;)V public synthetic fun (Ljava/lang/String;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -74,6 +82,9 @@ public class dev/gitlive/firebase/auth/AuthCredential { } public final class dev/gitlive/firebase/auth/AuthResult { + public fun (Lcom/google/firebase/auth/AuthResult;)V + public final fun getAdditionalUserInfo ()Ldev/gitlive/firebase/auth/AdditionalUserInfo; + public final fun getCredential ()Ldev/gitlive/firebase/auth/AuthCredential; public final fun getUser ()Ldev/gitlive/firebase/auth/FirebaseUser; } @@ -278,6 +289,7 @@ public final class dev/gitlive/firebase/auth/UserMetaData { public final class dev/gitlive/firebase/auth/android { public static final fun auth (Ldev/gitlive/firebase/Firebase;Ldev/gitlive/firebase/FirebaseApp;)Ldev/gitlive/firebase/auth/FirebaseAuth; + public static final fun getAndroid (Ldev/gitlive/firebase/auth/AdditionalUserInfo;)Lcom/google/firebase/auth/AdditionalUserInfo; public static final fun getAndroid (Ldev/gitlive/firebase/auth/AuthResult;)Lcom/google/firebase/auth/AuthResult; public static final fun getAndroid (Ldev/gitlive/firebase/auth/AuthTokenResult;)Lcom/google/firebase/auth/GetTokenResult; public static final fun getAndroid (Ldev/gitlive/firebase/auth/FirebaseAuth;)Lcom/google/firebase/auth/FirebaseAuth; diff --git a/firebase-auth/api/jvm/firebase-auth.api b/firebase-auth/api/jvm/firebase-auth.api index 170ac9afa..f629f1f01 100644 --- a/firebase-auth/api/jvm/firebase-auth.api +++ b/firebase-auth/api/jvm/firebase-auth.api @@ -51,6 +51,14 @@ public final class dev/gitlive/firebase/auth/ActionCodeSettings { public fun toString ()Ljava/lang/String; } +public final class dev/gitlive/firebase/auth/AdditionalUserInfo { + public fun ()V + public final fun getProfile ()Ljava/util/Map; + public final fun getProviderId ()Ljava/lang/String; + public final fun getUsername ()Ljava/lang/String; + public final fun isNewUser ()Z +} + public final class dev/gitlive/firebase/auth/AndroidPackageName { public fun (Ljava/lang/String;ZLjava/lang/String;)V public synthetic fun (Ljava/lang/String;ZLjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -74,6 +82,9 @@ public class dev/gitlive/firebase/auth/AuthCredential { } public final class dev/gitlive/firebase/auth/AuthResult { + public fun (Lcom/google/firebase/auth/AuthResult;)V + public final fun getAdditionalUserInfo ()Ldev/gitlive/firebase/auth/AdditionalUserInfo; + public final fun getCredential ()Ldev/gitlive/firebase/auth/AuthCredential; public final fun getUser ()Ldev/gitlive/firebase/auth/FirebaseUser; } diff --git a/firebase-auth/package.json b/firebase-auth/package.json index 23da53f91..364873210 100644 --- a/firebase-auth/package.json +++ b/firebase-auth/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-auth", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-auth.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4" 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 fec2e73a0..cc4f2957d 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 @@ -118,9 +118,29 @@ public actual class FirebaseAuth internal constructor(internal val android: com. public val AuthResult.android: com.google.firebase.auth.AuthResult get() = android -public actual class AuthResult internal constructor(internal val android: com.google.firebase.auth.AuthResult) { +public actual class AuthResult(internal val android: com.google.firebase.auth.AuthResult) { public actual val user: FirebaseUser? get() = android.user?.let { FirebaseUser(it) } + public actual val credential: AuthCredential? + get() = android.credential?.let { AuthCredential(it) } + public actual val additionalUserInfo: AdditionalUserInfo? + get() = android.additionalUserInfo?.let { AdditionalUserInfo(it) } +} + +public val AdditionalUserInfo.android: com.google.firebase.auth.AdditionalUserInfo + get() = android + +public actual class AdditionalUserInfo( + internal val android: com.google.firebase.auth.AdditionalUserInfo, +) { + public actual val providerId: String? + get() = android.providerId + public actual val username: String? + get() = android.username + public actual val profile: Map? + get() = android.profile + public actual val isNewUser: Boolean + get() = android.isNewUser } public val AuthTokenResult.android: com.google.firebase.auth.GetTokenResult get() = android diff --git a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt index f66ce3c03..2665d2eb8 100644 --- a/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/commonMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -41,6 +41,15 @@ public expect class FirebaseAuth { public expect class AuthResult { public val user: FirebaseUser? + public val credential: AuthCredential? + public val additionalUserInfo: AdditionalUserInfo? +} + +public expect class AdditionalUserInfo { + public val providerId: String? + public val username: String? + public val profile: Map? + public val isNewUser: Boolean } public expect class AuthTokenResult { 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 47db21755..96f259176 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 @@ -112,6 +112,22 @@ class FirebaseAuthTest { assertEquals(uid, result.user!!.uid) } + @Test + fun testAuthResultStructure() = runTest { + val uid = getTestUid("test@test.com", "test123") + val result = auth.signInWithEmailAndPassword("test@test.com", "test123") + assertNotNull(result.user, "User does not exist.") + assertEquals(uid, result.user!!.uid, "uid does not match.") + assertNull(result.credential, "Credential throws.") + // Just test if it does not throw + result.additionalUserInfo?.let { additionalUserInfo -> + additionalUserInfo.providerId + additionalUserInfo.username + additionalUserInfo.profile + additionalUserInfo.isNewUser + } + } + @Test fun testIsSignInWithEmailLink() { val validLink = "http://localhost:9099/emulator/action?mode=signIn&lang=en&oobCode=_vr0QcFcxcVeLZbrcU-GpTaZiuxlHquqdC8MSy0YM_vzWCTAQgV9Jq&apiKey=fake-api-key&continueUrl=https%3A%2F%2Fexample.com%2Fsignin" diff --git a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt index b659b0c68..30ff27bc1 100644 --- a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt +++ b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/auth.kt @@ -17,6 +17,7 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.Flow import platform.Foundation.NSError +import platform.Foundation.NSString import platform.Foundation.NSURL public val FirebaseAuth.ios: FIRAuth get() = FIRAuth.auth() @@ -31,7 +32,7 @@ public actual fun Firebase.auth(app: FirebaseApp): FirebaseAuth = FirebaseAuth( public actual class FirebaseAuth internal constructor(internal val ios: FIRAuth) { public actual val currentUser: FirebaseUser? - get() = ios.currentUser?.let { FirebaseUser(it) } + get() = ios.currentUser()?.let { FirebaseUser(it) } public actual val authStateChanged: Flow get() = callbackFlow { val handle = ios.addAuthStateDidChangeListener { _, user -> trySend(user?.let { FirebaseUser(it) }) } @@ -44,7 +45,7 @@ public actual class FirebaseAuth internal constructor(internal val ios: FIRAuth) } public actual var languageCode: String - get() = ios.languageCode ?: "" + get() = ios.languageCode() ?: "" set(value) { ios.setLanguageCode(value) } @@ -90,25 +91,53 @@ public actual class FirebaseAuth internal constructor(internal val ios: FIRAuth) public actual suspend fun checkActionCode(code: String): T { val result: FIRActionCodeInfo = ios.awaitResult { checkActionCode(code, it) } @Suppress("UNCHECKED_CAST") - return when (result.operation) { + return when (result.operation()) { FIRActionCodeOperationEmailLink -> SignInWithEmailLink - FIRActionCodeOperationVerifyEmail -> VerifyEmail(result.email!!) - FIRActionCodeOperationPasswordReset -> PasswordReset(result.email!!) - FIRActionCodeOperationRecoverEmail -> RecoverEmail(result.email!!, result.previousEmail!!) - FIRActionCodeOperationVerifyAndChangeEmail -> VerifyBeforeChangeEmail(result.email!!, result.previousEmail!!) - FIRActionCodeOperationRevertSecondFactorAddition -> RevertSecondFactorAddition(result.email!!, null) - FIRActionCodeOperationUnknown -> throw UnsupportedOperationException(result.operation.toString()) - else -> throw UnsupportedOperationException(result.operation.toString()) + FIRActionCodeOperationVerifyEmail -> VerifyEmail(result.email()) + FIRActionCodeOperationPasswordReset -> PasswordReset(result.email()) + FIRActionCodeOperationRecoverEmail -> RecoverEmail(result.email(), result.previousEmail()!!) + FIRActionCodeOperationVerifyAndChangeEmail -> VerifyBeforeChangeEmail(result.email(), result.previousEmail()!!) + FIRActionCodeOperationRevertSecondFactorAddition -> RevertSecondFactorAddition(result.email(), null) + FIRActionCodeOperationUnknown -> throw UnsupportedOperationException(result.operation().toString()) + else -> throw UnsupportedOperationException(result.operation().toString()) } as T } public actual fun useEmulator(host: String, port: Int): Unit = ios.useEmulatorWithHost(host, port.toLong()) } + public val AuthResult.ios: FIRAuthDataResult get() = ios -public actual class AuthResult internal constructor(internal val ios: FIRAuthDataResult) { +public actual class AuthResult(internal val ios: FIRAuthDataResult) { public actual val user: FirebaseUser? - get() = FirebaseUser(ios.user) + get() = FirebaseUser(ios.user()) + public actual val credential: AuthCredential? + get() = ios.credential()?.let { AuthCredential(it) } + public actual val additionalUserInfo: AdditionalUserInfo? + get() = ios.additionalUserInfo()?.let { AdditionalUserInfo(it) } +} + +public val AdditionalUserInfo.ios: FIRAdditionalUserInfo get() = ios + +public actual class AdditionalUserInfo( + internal val ios: FIRAdditionalUserInfo, +) { + public actual val providerId: String? + get() = ios.providerID() + public actual val username: String? + get() = ios.username() + public actual val profile: Map? + get() = ios.profile() + ?.mapNotNull { (key, value) -> + if (key is NSString && value != null) { + key.toString() to value + } else { + null + } + } + ?.toMap() + public actual val isNewUser: Boolean + get() = ios.newUser() } public val AuthTokenResult.ios: FIRAuthTokenResult get() = ios @@ -116,23 +145,23 @@ public actual class AuthTokenResult(internal val ios: FIRAuthTokenResult) { // actual val authTimestamp: Long // get() = ios.authDate public actual val claims: Map - get() = ios.claims.map { it.key.toString() to it.value as Any }.toMap() + get() = ios.claims().map { it.key.toString() to it.value as Any }.toMap() // actual val expirationTimestamp: Long // get() = ios.expirationDate // actual val issuedAtTimestamp: Long // get() = ios.issuedAtDate public actual val signInProvider: String? - get() = ios.signInProvider + get() = ios.signInProvider() public actual val token: String? - get() = ios.token + get() = ios.token() } internal fun ActionCodeSettings.toIos() = FIRActionCodeSettings().also { - it.URL = NSURL.URLWithString(url) + it.setURL(NSURL.URLWithString(url)) androidPackageName?.run { it.setAndroidPackageName(packageName, installIfNotAvailable, minimumVersion) } - it.dynamicLinkDomain = dynamicLinkDomain - it.handleCodeInApp = canHandleCodeInApp + it.setDynamicLinkDomain(dynamicLinkDomain) + it.setHandleCodeInApp(canHandleCodeInApp) iOSBundleId?.run { it.setIOSBundleID(this) } } @@ -184,47 +213,55 @@ internal suspend inline fun T.await(function: T.(callback: (NSError?) -> Uni } private fun NSError.toException() = when (domain) { + // codes from AuthErrors.swift: https://github.com/firebase/firebase-ios-sdk/blob/ + // 2f6ac4c2c61cd57c7ea727009e187b7e1163d613/FirebaseAuth/Sources/Swift/Utilities/ + // AuthErrors.swift#L51 FIRAuthErrorDomain -> when (code) { - FIRAuthErrorCodeInvalidActionCode, - FIRAuthErrorCodeExpiredActionCode, + 17030L, // AuthErrorCode.invalidActionCode + 17029L, // AuthErrorCode.expiredActionCode -> FirebaseAuthActionCodeException(toString()) - FIRAuthErrorCodeInvalidEmail -> FirebaseAuthEmailException(toString()) - - FIRAuthErrorCodeCaptchaCheckFailed, - FIRAuthErrorCodeInvalidPhoneNumber, - FIRAuthErrorCodeMissingPhoneNumber, - FIRAuthErrorCodeInvalidVerificationID, - FIRAuthErrorCodeInvalidVerificationCode, - FIRAuthErrorCodeMissingVerificationID, - FIRAuthErrorCodeMissingVerificationCode, - FIRAuthErrorCodeUserTokenExpired, - FIRAuthErrorCodeInvalidCredential, + 17008L, // AuthErrorCode.invalidEmail + -> FirebaseAuthEmailException(toString()) + + 17056L, // AuthErrorCode.captchaCheckFailed + 17042L, // AuthErrorCode.invalidPhoneNumber + 17041L, // AuthErrorCode.missingPhoneNumber + 17046L, // AuthErrorCode.invalidVerificationID + 17044L, // AuthErrorCode.invalidVerificationCode + 17045L, // AuthErrorCode.missingVerificationID + 17043L, // AuthErrorCode.missingVerificationCode + 17021L, // AuthErrorCode.userTokenExpired + 17004L, // AuthErrorCode.invalidCredential -> FirebaseAuthInvalidCredentialsException(toString()) - FIRAuthErrorCodeWeakPassword -> FirebaseAuthWeakPasswordException(toString()) + 17026L, // AuthErrorCode.weakPassword + -> FirebaseAuthWeakPasswordException(toString()) - FIRAuthErrorCodeInvalidUserToken -> FirebaseAuthInvalidUserException(toString()) + 17017L, // AuthErrorCode.invalidUserToken + -> FirebaseAuthInvalidUserException(toString()) - FIRAuthErrorCodeRequiresRecentLogin -> FirebaseAuthRecentLoginRequiredException(toString()) + 17014L, // AuthErrorCode.requiresRecentLogin + -> FirebaseAuthRecentLoginRequiredException(toString()) - FIRAuthErrorCodeSecondFactorAlreadyEnrolled, - FIRAuthErrorCodeSecondFactorRequired, - FIRAuthErrorCodeMaximumSecondFactorCountExceeded, - FIRAuthErrorCodeMultiFactorInfoNotFound, + 17087L, // AuthErrorCode.secondFactorAlreadyEnrolled + 17078L, // AuthErrorCode.secondFactorRequired + 17088L, // AuthErrorCode.maximumSecondFactorCountExceeded + 17084L, // AuthErrorCode.multiFactorInfoNotFound -> FirebaseAuthMultiFactorException(toString()) - FIRAuthErrorCodeEmailAlreadyInUse, - FIRAuthErrorCodeAccountExistsWithDifferentCredential, - FIRAuthErrorCodeCredentialAlreadyInUse, + 17007L, // AuthErrorCode.emailAlreadyInUse + 17012L, // AuthErrorCode.accountExistsWithDifferentCredential + 17025L, // AuthErrorCode.credentialAlreadyInUse -> FirebaseAuthUserCollisionException(toString()) - FIRAuthErrorCodeWebContextAlreadyPresented, - FIRAuthErrorCodeWebContextCancelled, - FIRAuthErrorCodeWebInternalError, + 17057L, // AuthErrorCode.webContextAlreadyPresented + 17058L, // AuthErrorCode.webContextCancelled + 17062L, // AuthErrorCode.webInternalError -> FirebaseAuthWebException(toString()) - FIRAuthErrorCodeNetworkError -> FirebaseNetworkException(toString()) + 17020L, // AuthErrorCode.networkError + -> FirebaseNetworkException(toString()) else -> FirebaseAuthException(toString()) } diff --git a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt index 08387a0e1..5a2084bb9 100644 --- a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt +++ b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/credentials.kt @@ -8,7 +8,7 @@ import cocoapods.FirebaseAuth.* public actual open class AuthCredential(public open val ios: FIRAuthCredential) { public actual val providerId: String - get() = ios.provider + get() = ios.provider() } public actual class PhoneAuthCredential(override val ios: FIRPhoneAuthCredential) : AuthCredential(ios) diff --git a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/multifactor.kt b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/multifactor.kt index f389b6abb..3d2c814e5 100644 --- a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/multifactor.kt +++ b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/multifactor.kt @@ -10,7 +10,7 @@ public val MultiFactor.ios: FIRMultiFactor get() = ios public actual class MultiFactor(internal val ios: FIRMultiFactor) { public actual val enrolledFactors: List - get() = ios.enrolledFactors.mapNotNull { info -> (info as? FIRMultiFactorInfo)?.let { MultiFactorInfo(it) } } + get() = ios.enrolledFactors().mapNotNull { info -> (info as? FIRMultiFactorInfo)?.let { MultiFactorInfo(it) } } public actual suspend fun enroll(multiFactorAssertion: MultiFactorAssertion, displayName: String?): Unit = ios.await { enrollWithAssertion(multiFactorAssertion.ios, displayName, it) } public actual suspend fun getSession(): MultiFactorSession = MultiFactorSession(ios.awaitResult { getSessionWithCompletion(completion = it) }) public actual suspend fun unenroll(multiFactorInfo: MultiFactorInfo): Unit = ios.await { unenrollWithInfo(multiFactorInfo.ios, it) } @@ -21,20 +21,20 @@ public val MultiFactorInfo.ios: FIRMultiFactorInfo get() = ios public actual class MultiFactorInfo(internal val ios: FIRMultiFactorInfo) { public actual val displayName: String? - get() = ios.displayName + get() = ios.displayName() public actual val enrollmentTime: Double - get() = ios.enrollmentDate.timeIntervalSinceReferenceDate + get() = ios.enrollmentDate().timeIntervalSinceReferenceDate public actual val factorId: String - get() = ios.factorID + get() = ios.factorID() public actual val uid: String - get() = ios.UID + get() = ios.UID() } public val MultiFactorAssertion.ios: FIRMultiFactorAssertion get() = ios public actual class MultiFactorAssertion(internal val ios: FIRMultiFactorAssertion) { public actual val factorId: String - get() = ios.factorID + get() = ios.factorID() } public val MultiFactorSession.ios: FIRMultiFactorSession get() = ios @@ -44,9 +44,9 @@ public actual class MultiFactorSession(internal val ios: FIRMultiFactorSession) public val MultiFactorResolver.ios: FIRMultiFactorResolver get() = ios public actual class MultiFactorResolver(internal val ios: FIRMultiFactorResolver) { - public actual val auth: FirebaseAuth = FirebaseAuth(ios.auth) - public actual val hints: List = ios.hints.mapNotNull { hint -> (hint as? FIRMultiFactorInfo)?.let { MultiFactorInfo(it) } } - public actual val session: MultiFactorSession = MultiFactorSession(ios.session) + public actual val auth: FirebaseAuth = FirebaseAuth(ios.auth()) + public actual val hints: List = ios.hints().mapNotNull { hint -> (hint as? FIRMultiFactorInfo)?.let { MultiFactorInfo(it) } } + public actual val session: MultiFactorSession = MultiFactorSession(ios.session()) public actual suspend fun resolveSignIn(assertion: MultiFactorAssertion): AuthResult = AuthResult(ios.awaitResult { resolveSignInWithAssertion(assertion.ios, it) }) } diff --git a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt index f2f6c67cc..d37fc20c0 100644 --- a/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt +++ b/firebase-auth/src/iosMain/kotlin/dev/gitlive/firebase/auth/user.kt @@ -14,27 +14,27 @@ public val FirebaseUser.ios: FIRUser get() = ios public actual class FirebaseUser internal constructor(internal val ios: FIRUser) { public actual val uid: String - get() = ios.uid + get() = ios.uid() public actual val displayName: String? - get() = ios.displayName + get() = ios.displayName() public actual val email: String? - get() = ios.email + get() = ios.email() public actual val phoneNumber: String? - get() = ios.phoneNumber + get() = ios.phoneNumber() public actual val photoURL: String? - get() = ios.photoURL?.absoluteString + get() = ios.photoURL()?.absoluteString public actual val isAnonymous: Boolean - get() = ios.anonymous + get() = ios.anonymous() public actual val isEmailVerified: Boolean - get() = ios.emailVerified + get() = ios.emailVerified() public actual val metaData: UserMetaData? - get() = UserMetaData(ios.metadata) + get() = UserMetaData(ios.metadata()) public actual val multiFactor: MultiFactor - get() = MultiFactor(ios.multiFactor) + get() = MultiFactor(ios.multiFactor()) public actual val providerData: List - get() = ios.providerData.mapNotNull { provider -> (provider as? FIRUserInfoProtocol)?.let { UserInfo(it) } } + get() = ios.providerData().mapNotNull { provider -> (provider as? FIRUserInfoProtocol)?.let { UserInfo(it) } } public actual val providerId: String - get() = ios.providerID + get() = ios.providerID() public actual suspend fun delete(): Unit = ios.await { deleteWithCompletion(it) } @@ -85,24 +85,24 @@ public val UserInfo.ios: FIRUserInfoProtocol get() = ios public actual class UserInfo(internal val ios: FIRUserInfoProtocol) { public actual val displayName: String? - get() = ios.displayName + get() = ios.displayName() public actual val email: String? - get() = ios.email + get() = ios.email() public actual val phoneNumber: String? - get() = ios.phoneNumber + get() = ios.phoneNumber() public actual val photoURL: String? - get() = ios.photoURL?.absoluteString + get() = ios.photoURL()?.absoluteString public actual val providerId: String - get() = ios.providerID + get() = ios.providerID() public actual val uid: String - get() = ios.uid + get() = ios.uid() } public val UserMetaData.ios: FIRUserMetadata get() = ios public actual class UserMetaData(internal val ios: FIRUserMetadata) { public actual val creationTime: Double? - get() = ios.creationDate?.timeIntervalSinceReferenceDate + get() = ios.creationDate()?.timeIntervalSinceReferenceDate public actual val lastSignInTime: Double? - get() = ios.lastSignInDate?.timeIntervalSinceReferenceDate + get() = ios.lastSignInDate()?.timeIntervalSinceReferenceDate } 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 a222d4d6e..4d0223d28 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 @@ -33,8 +33,10 @@ import kotlinx.coroutines.await import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import kotlin.js.Json import kotlin.js.json import dev.gitlive.firebase.auth.externals.AuthResult as JsAuthResult +import dev.gitlive.firebase.auth.externals.AdditionalUserInfo as JsAdditionalUserInfo public actual val Firebase.auth: FirebaseAuth get() = rethrow { FirebaseAuth(getAuth()) } @@ -133,9 +135,34 @@ public actual class FirebaseAuth internal constructor(internal val js: Auth) { public val AuthResult.js: JsAuthResult get() = js -public actual class AuthResult internal constructor(internal val js: JsAuthResult) { +public actual class AuthResult(internal val js: JsAuthResult) { public actual val user: FirebaseUser? get() = rethrow { js.user?.let { FirebaseUser(it) } } + public actual val credential: AuthCredential? + get() = rethrow { js.credential?.let { AuthCredential(it) } } + public actual val additionalUserInfo: AdditionalUserInfo? + get() = rethrow { js.additionalUserInfo?.let { AdditionalUserInfo(it) } } +} + +public val AdditionalUserInfo.js: JsAdditionalUserInfo get() = js + +public actual class AdditionalUserInfo( + internal val js: JsAdditionalUserInfo, +) { + public actual val providerId: String? + get() = js.providerId + public actual val username: String? + get() = js.username + public actual val profile: Map? + get() = rethrow { + val profile = js.profile ?: return@rethrow null + val entries = js("Object.entries") as (Json) -> Array> + return@rethrow entries + .invoke(profile) + .associate { entry -> entry[0] as String to entry[1] } + } + public actual val isNewUser: Boolean + get() = js.newUser } public val AuthTokenResult.js: IdTokenResult get() = js diff --git a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/externals/auth.kt b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/externals/auth.kt index e535e6c22..20c3f14c3 100644 --- a/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/externals/auth.kt +++ b/firebase-auth/src/jsMain/kotlin/dev/gitlive/firebase/auth/externals/auth.kt @@ -170,6 +170,14 @@ public external interface AuthResult { public val credential: AuthCredential? public val operationType: String? public val user: User? + public val additionalUserInfo: AdditionalUserInfo? +} + +public external interface AdditionalUserInfo { + public val providerId: String? + public val username: String? + public val profile: Json? + public val newUser: Boolean } public external interface AuthCredential { 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 3eda71f6b..21043d4d4 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 @@ -18,7 +18,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.tasks.await -public actual val Firebase.auth +public actual val Firebase.auth: FirebaseAuth get() = FirebaseAuth(com.google.firebase.auth.FirebaseAuth.getInstance()) public actual fun Firebase.auth(app: FirebaseApp) = @@ -127,9 +127,24 @@ public actual class FirebaseAuth internal constructor(internal val android: com. public val AuthResult.android: com.google.firebase.auth.AuthResult get() = android -public actual class AuthResult internal constructor(internal val android: com.google.firebase.auth.AuthResult) { +public actual class AuthResult(internal val android: com.google.firebase.auth.AuthResult) { public actual val user: FirebaseUser? get() = android.user?.let { FirebaseUser(it) } + public actual val credential: AuthCredential? + get() = throw NotImplementedError() + public actual val additionalUserInfo: AdditionalUserInfo? + get() = throw NotImplementedError() +} + +public actual class AdditionalUserInfo { + public actual val providerId: String? + get() = throw NotImplementedError() + public actual val username: String? + get() = throw NotImplementedError() + public actual val profile: Map? + get() = throw NotImplementedError() + public actual val isNewUser: Boolean + get() = throw NotImplementedError() } public val AuthTokenResult.android: com.google.firebase.auth.GetTokenResult get() = android diff --git a/firebase-common-internal/package.json b/firebase-common-internal/package.json index c1a555b9e..2d0738776 100644 --- a/firebase-common-internal/package.json +++ b/firebase-common-internal/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-common-internal", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-common-internal.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-multiplatform-sdk", "dependencies": { - "@gitlive/firebase-common": "2.0.0", + "@gitlive/firebase-common": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4", diff --git a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt index 474aeea57..767688ad0 100644 --- a/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt +++ b/firebase-common-internal/src/commonMain/kotlin/dev/gitlive/firebase/internal/encoders.kt @@ -6,7 +6,6 @@ package dev.gitlive.firebase.internal import dev.gitlive.firebase.EncodeSettings import dev.gitlive.firebase.FirebaseEncoder -import dev.gitlive.firebase.ValueWithSerializer import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.descriptors.SerialDescriptor @@ -63,14 +62,7 @@ public inline fun encodeAsObject(value: T, buildSettings: Enco @PublishedApi internal inline fun encode(value: T, encodeSettings: EncodeSettings): Any? = value?.let { FirebaseEncoderImpl(encodeSettings).apply { - if (it is ValueWithSerializer<*> && it.value is T) { - @Suppress("UNCHECKED_CAST") - (it as ValueWithSerializer).let { - encodeSerializableValue(it.serializer, it.value) - } - } else { - encodeSerializableValue(it.firebaseSerializer(), it) - } + encodeSerializableValue(it.firebaseSerializer(), it) }.value } @@ -164,12 +156,6 @@ internal open class FirebaseCompositeEncoder( private val set: (descriptor: SerialDescriptor, index: Int, value: Any?) -> Unit, ) : CompositeEncoder { -// private fun SerializationStrategy.toFirebase(): SerializationStrategy = when(descriptor.kind) { -// StructureKind.MAP -> FirebaseMapSerializer(descriptor.getElementDescriptor(1)) as SerializationStrategy -// StructureKind.LIST -> FirebaseListSerializer(descriptor.getElementDescriptor(0)) as SerializationStrategy -// else -> this -// } - override val serializersModule: SerializersModule = settings.serializersModule override fun endStructure(descriptor: SerialDescriptor): Unit = end() diff --git a/firebase-common-internal/src/commonTest/kotlin/dev/gitlive/firebase/internal/EncodersTest.kt b/firebase-common-internal/src/commonTest/kotlin/dev/gitlive/firebase/internal/EncodersTest.kt index 145d57b74..2017f8dc6 100644 --- a/firebase-common-internal/src/commonTest/kotlin/dev/gitlive/firebase/internal/EncodersTest.kt +++ b/firebase-common-internal/src/commonTest/kotlin/dev/gitlive/firebase/internal/EncodersTest.kt @@ -262,6 +262,47 @@ class EncodersTest { assertEquals(nestedClass, decoded) } + @Test + fun encodeDecodeNestedClassWithEmptyCollections() { + val module = SerializersModule { + polymorphic(AbstractClass::class, AbstractClass.serializer()) { + subclass(ImplementedClass::class, ImplementedClass.serializer()) + } + } + + val testData = TestData(mapOf(), mapOf(), true, null, ValueClass(42)) + val sealedClass: SealedClass = SealedClass.Test("value") + val abstractClass: AbstractClass = ImplementedClass("value", true) + val nestedClass = NestedClass(testData, sealedClass, abstractClass, listOf(), listOf(), listOf(), mapOf(), mapOf(), mapOf()) + val encoded = encode(NestedClass.serializer(), nestedClass) { + encodeDefaults = true + serializersModule = module + } + + val testDataEncoded = nativeMapOf("map" to nativeMapOf(), "otherMap" to nativeMapOf(), "bool" to true, "nullableBool" to null, "valueClass" to 42) + val sealedEncoded = nativeMapOf("type" to "test", "value" to "value") + val abstractEncoded = nativeMapOf("type" to "implemented", "abstractValue" to "value", "otherValue" to true) + nativeAssertEquals( + nativeMapOf( + "testData" to testDataEncoded, + "sealed" to sealedEncoded, + "abstract" to abstractEncoded, + "testDataList" to nativeListOf(), + "sealedList" to nativeListOf(), + "abstractList" to nativeListOf(), + "testDataMap" to nativeMapOf(), + "sealedMap" to nativeMapOf(), + "abstractMap" to nativeMapOf(), + ), + encoded, + ) + + val decoded = decode(NestedClass.serializer(), encoded) { + serializersModule = module + } + assertEquals(nestedClass, decoded) + } + @Test fun reencodeTransformationList() { val reencoded = reencodeTransformation>(nativeListOf("One", "Two", "Three")) { diff --git a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt index 7121128d0..091d9c2da 100644 --- a/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt +++ b/firebase-common-internal/src/jsMain/kotlin/dev/gitlive/firebase/internal/_encoders.kt @@ -28,7 +28,7 @@ internal actual fun FirebaseEncoderImpl.structureEncoder(descriptor: SerialDescr else -> TODO("The firebase-kotlin-sdk does not support $descriptor for serialization yet") } -private fun FirebaseEncoderImpl.encodeAsList(descriptor: SerialDescriptor): FirebaseCompositeEncoder = Array(descriptor.elementsCount) { null } +private fun FirebaseEncoderImpl.encodeAsList(descriptor: SerialDescriptor): FirebaseCompositeEncoder = Array(descriptor.elementsCount - 1) { null } .also { value = it } .let { FirebaseCompositeEncoder(settings) { _, index, value -> it[index] = value } } private fun FirebaseEncoderImpl.encodeAsMap(descriptor: SerialDescriptor): FirebaseCompositeEncoder = json() diff --git a/firebase-common/api/android/firebase-common.api b/firebase-common/api/android/firebase-common.api index 9724b86d4..14df6bc61 100644 --- a/firebase-common/api/android/firebase-common.api +++ b/firebase-common/api/android/firebase-common.api @@ -51,20 +51,3 @@ public final class dev/gitlive/firebase/FirebaseEncoder$DefaultImpls { public static fun encodeSerializableValue (Ldev/gitlive/firebase/FirebaseEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V } -public final class dev/gitlive/firebase/ValueWithSerializer { - public fun (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)V - public final fun component1 ()Ljava/lang/Object; - public final fun component2 ()Lkotlinx/serialization/SerializationStrategy; - public final fun copy (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)Ldev/gitlive/firebase/ValueWithSerializer; - public static synthetic fun copy$default (Ldev/gitlive/firebase/ValueWithSerializer;Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;ILjava/lang/Object;)Ldev/gitlive/firebase/ValueWithSerializer; - public fun equals (Ljava/lang/Object;)Z - public final fun getSerializer ()Lkotlinx/serialization/SerializationStrategy; - public final fun getValue ()Ljava/lang/Object; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class dev/gitlive/firebase/ValueWithSerializerKt { - public static final fun withSerializer (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)Ljava/lang/Object; -} - diff --git a/firebase-common/api/jvm/firebase-common.api b/firebase-common/api/jvm/firebase-common.api index 9724b86d4..14df6bc61 100644 --- a/firebase-common/api/jvm/firebase-common.api +++ b/firebase-common/api/jvm/firebase-common.api @@ -51,20 +51,3 @@ public final class dev/gitlive/firebase/FirebaseEncoder$DefaultImpls { public static fun encodeSerializableValue (Ldev/gitlive/firebase/FirebaseEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V } -public final class dev/gitlive/firebase/ValueWithSerializer { - public fun (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)V - public final fun component1 ()Ljava/lang/Object; - public final fun component2 ()Lkotlinx/serialization/SerializationStrategy; - public final fun copy (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)Ldev/gitlive/firebase/ValueWithSerializer; - public static synthetic fun copy$default (Ldev/gitlive/firebase/ValueWithSerializer;Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;ILjava/lang/Object;)Ldev/gitlive/firebase/ValueWithSerializer; - public fun equals (Ljava/lang/Object;)Z - public final fun getSerializer ()Lkotlinx/serialization/SerializationStrategy; - public final fun getValue ()Ljava/lang/Object; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - -public final class dev/gitlive/firebase/ValueWithSerializerKt { - public static final fun withSerializer (Ljava/lang/Object;Lkotlinx/serialization/SerializationStrategy;)Ljava/lang/Object; -} - diff --git a/firebase-common/package.json b/firebase-common/package.json index ac1374d45..b2edcd792 100644 --- a/firebase-common/package.json +++ b/firebase-common/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-common", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-common.js", "scripts": { diff --git a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/ValueWithSerializer.kt b/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/ValueWithSerializer.kt deleted file mode 100644 index 84ed4713b..000000000 --- a/firebase-common/src/commonMain/kotlin/dev/gitlive/firebase/ValueWithSerializer.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.gitlive.firebase - -import kotlinx.serialization.SerializationStrategy - -/** - * An extension which which serializer to use for value. Handy in updating fields by name or path - * where using annotation is not possible - * @return a value with a custom serializer. - */ -public fun T.withSerializer(serializer: SerializationStrategy): Any = ValueWithSerializer(this, serializer) -public data class ValueWithSerializer(val value: T, val serializer: SerializationStrategy) diff --git a/firebase-config/build.gradle.kts b/firebase-config/build.gradle.kts index a28086463..f942264e4 100644 --- a/firebase-config/build.gradle.kts +++ b/firebase-config/build.gradle.kts @@ -29,7 +29,6 @@ android { } compileOptions { - isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } @@ -45,10 +44,6 @@ android { } } -dependencies { - coreLibraryDesugaring(libs.android.desugarjdk) -} - val supportIosTarget = project.property("skipIosTarget") != "true" kotlin { diff --git a/firebase-config/package.json b/firebase-config/package.json index 69582d9fc..c016fe275 100644 --- a/firebase-config/package.json +++ b/firebase-config/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-config", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-config.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4" diff --git a/firebase-crashlytics/package.json b/firebase-crashlytics/package.json index 989eda9b0..4c8a35d7f 100644 --- a/firebase-crashlytics/package.json +++ b/firebase-crashlytics/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-crashlytics", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-crashlytics.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.6.10", "kotlinx-coroutines-core": "1.6.1-native-mt" diff --git a/firebase-database/package.json b/firebase-database/package.json index 228ba4282..7e01a2415 100644 --- a/firebase-database/package.json +++ b/firebase-database/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-database", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-database.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4" diff --git a/firebase-firestore/api/android/firebase-firestore.api b/firebase-firestore/api/android/firebase-firestore.api index 8a0aa2e9a..9cf847e6b 100644 --- a/firebase-firestore/api/android/firebase-firestore.api +++ b/firebase-firestore/api/android/firebase-firestore.api @@ -68,16 +68,15 @@ public final class dev/gitlive/firebase/firestore/DocumentReference { 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 fun toString ()Ljava/lang/String; + public final fun update (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; 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; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun updateEncoded (Ldev/gitlive/firebase/internal/EncodedObject;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateEncodedFieldPathsAndValues (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateEncodedFieldsAndValues (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateFieldPaths ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun updateFieldPaths$default (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public final fun updateFields ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun updateFields$default (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public final fun updateFieldPaths (Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFieldPaths ([Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFields (Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFields ([Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class dev/gitlive/firebase/firestore/DocumentReference$Companion { @@ -161,6 +160,13 @@ public final class dev/gitlive/firebase/firestore/FieldValue$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class dev/gitlive/firebase/firestore/FieldValueBuilder { + public final fun addEncoded (Ljava/lang/Object;)V + public final fun addWithStrategy (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun getBuildSettings ()Lkotlin/jvm/functions/Function1; + public final fun setBuildSettings (Lkotlin/jvm/functions/Function1;)V +} + public final class dev/gitlive/firebase/firestore/FieldValueSerializer : kotlinx/serialization/KSerializer { public static final field INSTANCE Ldev/gitlive/firebase/firestore/FieldValueSerializer; public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ldev/gitlive/firebase/firestore/FieldValue; @@ -170,6 +176,15 @@ public final class dev/gitlive/firebase/firestore/FieldValueSerializer : kotlinx public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } +public final class dev/gitlive/firebase/firestore/FieldsAndValuesBuilder { + public final fun getBuildSettings ()Lkotlin/jvm/functions/Function1; + public final fun setBuildSettings (Lkotlin/jvm/functions/Function1;)V + public final fun to (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun to (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun toEncoded (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)V + public final fun toEncoded (Ljava/lang/String;Ljava/lang/Object;)V +} + public abstract class dev/gitlive/firebase/firestore/Filter { } @@ -182,6 +197,7 @@ public final class dev/gitlive/firebase/firestore/Filter$And : dev/gitlive/fireb } public final class dev/gitlive/firebase/firestore/Filter$Field : dev/gitlive/firebase/firestore/Filter$WithConstraint { + public fun (Ljava/lang/String;Ldev/gitlive/firebase/firestore/WhereConstraint;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ldev/gitlive/firebase/firestore/WhereConstraint; public fun equals (Ljava/lang/Object;)Z @@ -200,6 +216,7 @@ public final class dev/gitlive/firebase/firestore/Filter$Or : dev/gitlive/fireba } public final class dev/gitlive/firebase/firestore/Filter$Path : dev/gitlive/firebase/firestore/Filter$WithConstraint { + public fun (Ldev/gitlive/firebase/firestore/FieldPath;Ldev/gitlive/firebase/firestore/WhereConstraint;)V public final fun component1 ()Ldev/gitlive/firebase/firestore/FieldPath; public final fun component2 ()Ldev/gitlive/firebase/firestore/WhereConstraint; public fun equals (Ljava/lang/Object;)Z @@ -217,27 +234,33 @@ public final class dev/gitlive/firebase/firestore/FilterBuilder { public final fun all ([Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter; public final fun and (Ldev/gitlive/firebase/firestore/Filter;Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter$And; public final fun any ([Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter; - public final fun contains (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun contains (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun containsAny (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun containsAny (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun equalTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun equalTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThan (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThan (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThanOrEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun inArray (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun inArray (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThan (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThan (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThanOrEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notInArray (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notInArray (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun contains (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun contains (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun containsAny (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun containsAny (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun equalTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun equalTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun getBuildSettings ()Lkotlin/jvm/functions/Function1; + public final fun greaterThan (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThan (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThanOrEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun inArray (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun inArray (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNotNull (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNotNull (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNull (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNull (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThan (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThan (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThanOrEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notInArray (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notInArray (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; public final fun or (Ldev/gitlive/firebase/firestore/Filter;Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter$Or; + public final fun setBuildSettings (Lkotlin/jvm/functions/Function1;)V } public final class dev/gitlive/firebase/firestore/FirebaseFirestore { @@ -333,12 +356,6 @@ public final class dev/gitlive/firebase/firestore/GeoPointSerializer : kotlinx/s public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } -public final class dev/gitlive/firebase/firestore/HelpersKt { - public static final fun encodeFieldAndValue ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun performUpdateFieldPaths ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun performUpdateFields ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ljava/util/List; -} - public abstract interface class dev/gitlive/firebase/firestore/LocalCacheSettings { } @@ -424,8 +441,12 @@ public final class dev/gitlive/firebase/firestore/MemoryGarbageCollectorSettings public class dev/gitlive/firebase/firestore/Query { public static final field Companion Ldev/gitlive/firebase/firestore/Query$Companion; public final fun endAt (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; + public final fun endAt (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun endAt (Lkotlin/jvm/functions/Function1;[Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun endAt ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun endBefore (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; + public final fun endBefore (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun endBefore (Lkotlin/jvm/functions/Function1;[Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun endBefore ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun get (Ldev/gitlive/firebase/firestore/Source;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun get$default (Ldev/gitlive/firebase/firestore/Query;Ldev/gitlive/firebase/firestore/Source;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; @@ -438,8 +459,12 @@ public class dev/gitlive/firebase/firestore/Query { 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 final fun startAfter (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAfter (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAfter (Lkotlin/jvm/functions/Function1;[Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun startAfter ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun startAt (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAt (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAt (Lkotlin/jvm/functions/Function1;[Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun startAt ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun where (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; } @@ -556,17 +581,16 @@ public final class dev/gitlive/firebase/firestore/Transaction { public fun toString ()Ljava/lang/String; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/lang/Object;Z)Ldev/gitlive/firebase/firestore/Transaction; + public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Z)Ldev/gitlive/firebase/firestore/Transaction; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; public final fun updateEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateEncodedFieldPathsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateEncodedFieldsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; - public static synthetic fun updateFieldPaths$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; - public static synthetic fun updateFields$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; } public final class dev/gitlive/firebase/firestore/Transaction$Companion { @@ -576,117 +600,102 @@ public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint { } public final class dev/gitlive/firebase/firestore/WhereConstraint$ArrayContains : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$ArrayContainsAny : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { + public fun (Ljava/util/List;)V public final fun component1 ()Ljava/util/List; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$EqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForArray : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValues ()Ljava/util/List; public abstract fun getValues ()Ljava/util/List; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForArray$DefaultImpls { - public static fun getSafeValues (Ldev/gitlive/firebase/firestore/WhereConstraint$ForArray;)Ljava/util/List; -} - public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValue ()Ljava/lang/Object; public abstract fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject$DefaultImpls { - public static fun getSafeValue (Ldev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject;)Ljava/lang/Object; -} - public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForObject : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValue ()Ljava/lang/Object; public abstract fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForObject$DefaultImpls { - public static fun getSafeValue (Ldev/gitlive/firebase/firestore/WhereConstraint$ForObject;)Ljava/lang/Object; -} - public final class dev/gitlive/firebase/firestore/WhereConstraint$GreaterThan : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$GreaterThanOrEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$InArray : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { + public fun (Ljava/util/List;)V public final fun component1 ()Ljava/util/List; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$LessThan : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$LessThanOrEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$NotEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$NotInArray : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { + public fun (Ljava/util/List;)V public final fun component1 ()Ljava/util/List; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -710,16 +719,15 @@ public final class dev/gitlive/firebase/firestore/WriteBatch { public static synthetic fun set$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;[Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun setEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;Ldev/gitlive/firebase/firestore/internal/SetOptions;)Ldev/gitlive/firebase/firestore/WriteBatch; public fun toString ()Ljava/lang/String; + public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Z)Ldev/gitlive/firebase/firestore/WriteBatch; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun updateEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateEncodedFieldPathsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateEncodedFieldsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; - public static synthetic fun updateField$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; - public static synthetic fun updateFieldPath$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; } public final class dev/gitlive/firebase/firestore/WriteBatch$Companion { diff --git a/firebase-firestore/api/jvm/firebase-firestore.api b/firebase-firestore/api/jvm/firebase-firestore.api index 33a64aef7..6b5abda69 100644 --- a/firebase-firestore/api/jvm/firebase-firestore.api +++ b/firebase-firestore/api/jvm/firebase-firestore.api @@ -68,16 +68,15 @@ public final class dev/gitlive/firebase/firestore/DocumentReference { 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 fun toString ()Ljava/lang/String; + public final fun update (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; 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; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun updateEncoded (Ldev/gitlive/firebase/internal/EncodedObject;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateEncodedFieldPathsAndValues (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateEncodedFieldsAndValues (Ljava/util/List;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun updateFieldPaths ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun updateFieldPaths$default (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public final fun updateFields ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun updateFields$default (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public final fun updateFieldPaths (Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFieldPaths ([Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFields (Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateFields ([Lkotlin/Pair;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class dev/gitlive/firebase/firestore/DocumentReference$Companion { @@ -161,6 +160,13 @@ public final class dev/gitlive/firebase/firestore/FieldValue$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class dev/gitlive/firebase/firestore/FieldValueBuilder { + public final fun addEncoded (Ljava/lang/Object;)V + public final fun addWithStrategy (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun getBuildSettings ()Lkotlin/jvm/functions/Function1; + public final fun setBuildSettings (Lkotlin/jvm/functions/Function1;)V +} + public final class dev/gitlive/firebase/firestore/FieldValueSerializer : kotlinx/serialization/KSerializer { public static final field INSTANCE Ldev/gitlive/firebase/firestore/FieldValueSerializer; public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ldev/gitlive/firebase/firestore/FieldValue; @@ -170,6 +176,15 @@ public final class dev/gitlive/firebase/firestore/FieldValueSerializer : kotlinx public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } +public final class dev/gitlive/firebase/firestore/FieldsAndValuesBuilder { + public final fun getBuildSettings ()Lkotlin/jvm/functions/Function1; + public final fun setBuildSettings (Lkotlin/jvm/functions/Function1;)V + public final fun to (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun to (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V + public final fun toEncoded (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)V + public final fun toEncoded (Ljava/lang/String;Ljava/lang/Object;)V +} + public abstract class dev/gitlive/firebase/firestore/Filter { } @@ -182,6 +197,7 @@ public final class dev/gitlive/firebase/firestore/Filter$And : dev/gitlive/fireb } public final class dev/gitlive/firebase/firestore/Filter$Field : dev/gitlive/firebase/firestore/Filter$WithConstraint { + public fun (Ljava/lang/String;Ldev/gitlive/firebase/firestore/WhereConstraint;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ldev/gitlive/firebase/firestore/WhereConstraint; public fun equals (Ljava/lang/Object;)Z @@ -200,6 +216,7 @@ public final class dev/gitlive/firebase/firestore/Filter$Or : dev/gitlive/fireba } public final class dev/gitlive/firebase/firestore/Filter$Path : dev/gitlive/firebase/firestore/Filter$WithConstraint { + public fun (Ldev/gitlive/firebase/firestore/FieldPath;Ldev/gitlive/firebase/firestore/WhereConstraint;)V public final fun component1 ()Ldev/gitlive/firebase/firestore/FieldPath; public final fun component2 ()Ldev/gitlive/firebase/firestore/WhereConstraint; public fun equals (Ljava/lang/Object;)Z @@ -217,27 +234,33 @@ public final class dev/gitlive/firebase/firestore/FilterBuilder { public final fun all ([Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter; public final fun and (Ldev/gitlive/firebase/firestore/Filter;Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter$And; public final fun any ([Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter; - public final fun contains (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun contains (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun containsAny (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun containsAny (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun equalTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun equalTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThan (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThan (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun greaterThanOrEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun inArray (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun inArray (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThan (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThan (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun lessThanOrEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notEqualTo (Ljava/lang/String;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notInArray (Ldev/gitlive/firebase/firestore/FieldPath;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; - public final fun notInArray (Ljava/lang/String;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun contains (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun contains (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun containsAny (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun containsAny (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun equalTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun equalTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun getBuildSettings ()Lkotlin/jvm/functions/Function1; + public final fun greaterThan (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThan (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun greaterThanOrEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun inArray (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun inArray (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNotNull (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNotNull (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNull (Ldev/gitlive/firebase/firestore/FieldPath;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun isNull (Ljava/lang/String;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThan (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThan (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThanOrEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun lessThanOrEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notEqualTo (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notEqualTo (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notInArray (Ldev/gitlive/firebase/firestore/FieldPath;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; + public final fun notInArray (Ljava/lang/String;Lkotlinx/serialization/SerializationStrategy;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Filter$WithConstraint; public final fun or (Ldev/gitlive/firebase/firestore/Filter;Ldev/gitlive/firebase/firestore/Filter;)Ldev/gitlive/firebase/firestore/Filter$Or; + public final fun setBuildSettings (Lkotlin/jvm/functions/Function1;)V } public final class dev/gitlive/firebase/firestore/FirebaseFirestore { @@ -333,12 +356,6 @@ public final class dev/gitlive/firebase/firestore/GeoPointSerializer : kotlinx/s public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V } -public final class dev/gitlive/firebase/firestore/HelpersKt { - public static final fun encodeFieldAndValue ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun performUpdateFieldPaths ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ljava/util/List; - public static final fun performUpdateFields ([Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ljava/util/List; -} - public abstract interface class dev/gitlive/firebase/firestore/LocalCacheSettings { } @@ -424,8 +441,12 @@ public final class dev/gitlive/firebase/firestore/MemoryGarbageCollectorSettings public class dev/gitlive/firebase/firestore/Query { public static final field Companion Ldev/gitlive/firebase/firestore/Query$Companion; public final fun endAt (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; + public final fun endAt (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun endAt (Lkotlin/jvm/functions/Function1;[Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun endAt ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun endBefore (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; + public final fun endBefore (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun endBefore (Lkotlin/jvm/functions/Function1;[Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun endBefore ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun get (Ldev/gitlive/firebase/firestore/Source;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun get$default (Ldev/gitlive/firebase/firestore/Query;Ldev/gitlive/firebase/firestore/Source;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; @@ -438,8 +459,12 @@ public class dev/gitlive/firebase/firestore/Query { 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 final fun startAfter (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAfter (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAfter (Lkotlin/jvm/functions/Function1;[Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun startAfter ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun startAt (Ldev/gitlive/firebase/firestore/DocumentSnapshot;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAt (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; + public final fun startAt (Lkotlin/jvm/functions/Function1;[Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun startAt ([Ljava/lang/Object;)Ldev/gitlive/firebase/firestore/Query; public final fun where (Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Query; } @@ -556,17 +581,16 @@ public final class dev/gitlive/firebase/firestore/Transaction { public fun toString ()Ljava/lang/String; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/lang/Object;Z)Ldev/gitlive/firebase/firestore/Transaction; + public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Z)Ldev/gitlive/firebase/firestore/Transaction; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; public final fun updateEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateEncodedFieldPathsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateEncodedFieldsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; - public static synthetic fun updateFieldPaths$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; - public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/Transaction; - public static synthetic fun updateFields$default (Ldev/gitlive/firebase/firestore/Transaction;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFieldPaths (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; + public final fun updateFields (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/Transaction; } public final class dev/gitlive/firebase/firestore/Transaction$Companion { @@ -576,117 +600,102 @@ public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint { } public final class dev/gitlive/firebase/firestore/WhereConstraint$ArrayContains : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$ArrayContainsAny : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { + public fun (Ljava/util/List;)V public final fun component1 ()Ljava/util/List; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$EqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForArray : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValues ()Ljava/util/List; public abstract fun getValues ()Ljava/util/List; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForArray$DefaultImpls { - public static fun getSafeValues (Ldev/gitlive/firebase/firestore/WhereConstraint$ForArray;)Ljava/util/List; -} - public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValue ()Ljava/lang/Object; public abstract fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject$DefaultImpls { - public static fun getSafeValue (Ldev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject;)Ljava/lang/Object; -} - public abstract interface class dev/gitlive/firebase/firestore/WhereConstraint$ForObject : dev/gitlive/firebase/firestore/WhereConstraint { - public abstract fun getSafeValue ()Ljava/lang/Object; public abstract fun getValue ()Ljava/lang/Object; } -public final class dev/gitlive/firebase/firestore/WhereConstraint$ForObject$DefaultImpls { - public static fun getSafeValue (Ldev/gitlive/firebase/firestore/WhereConstraint$ForObject;)Ljava/lang/Object; -} - public final class dev/gitlive/firebase/firestore/WhereConstraint$GreaterThan : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$GreaterThanOrEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$InArray : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { + public fun (Ljava/util/List;)V public final fun component1 ()Ljava/util/List; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$LessThan : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$LessThanOrEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$NotEqualTo : dev/gitlive/firebase/firestore/WhereConstraint$ForNullableObject { + public fun (Ljava/lang/Object;)V public final fun component1 ()Ljava/lang/Object; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValue ()Ljava/lang/Object; public fun getValue ()Ljava/lang/Object; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public final class dev/gitlive/firebase/firestore/WhereConstraint$NotInArray : dev/gitlive/firebase/firestore/WhereConstraint$ForArray { + public fun (Ljava/util/List;)V public final fun component1 ()Ljava/util/List; public fun equals (Ljava/lang/Object;)Z - public fun getSafeValues ()Ljava/util/List; public fun getValues ()Ljava/util/List; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -710,16 +719,15 @@ public final class dev/gitlive/firebase/firestore/WriteBatch { public static synthetic fun set$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;[Ljava/lang/String;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun setEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;Ldev/gitlive/firebase/firestore/internal/SetOptions;)Ldev/gitlive/firebase/firestore/WriteBatch; public fun toString ()Ljava/lang/String; + public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun update (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Z)Ldev/gitlive/firebase/firestore/WriteBatch; public static synthetic fun update$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; public final fun updateEncoded (Ldev/gitlive/firebase/firestore/DocumentReference;Ldev/gitlive/firebase/internal/EncodedObject;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateEncodedFieldPathsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateEncodedFieldsAndValues (Ldev/gitlive/firebase/firestore/DocumentReference;Ljava/util/List;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; - public static synthetic fun updateField$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; - public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;)Ldev/gitlive/firebase/firestore/WriteBatch; - public static synthetic fun updateFieldPath$default (Ldev/gitlive/firebase/firestore/WriteBatch;Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateField (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;Lkotlin/jvm/functions/Function1;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; + public final fun updateFieldPath (Ldev/gitlive/firebase/firestore/DocumentReference;[Lkotlin/Pair;)Ldev/gitlive/firebase/firestore/WriteBatch; } public final class dev/gitlive/firebase/firestore/WriteBatch$Companion { diff --git a/firebase-firestore/package.json b/firebase-firestore/package.json index 8efd2dd91..882cd52be 100644 --- a/firebase-firestore/package.json +++ b/firebase-firestore/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-firestore", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-firestore.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4" diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index cb91ffd74..2cc2047a0 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -2,7 +2,6 @@ package dev.gitlive.firebase.firestore.internal import com.google.android.gms.tasks.TaskExecutors import com.google.firebase.firestore.MetadataChanges -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot import dev.gitlive.firebase.firestore.Source @@ -44,19 +43,12 @@ internal actual class NativeDocumentReference actual constructor(actual val nati android.update(encodedData.android).await() } - actual suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) { + actual suspend fun updateEncoded(encodedFieldsAndValues: List) { encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() }?.let { - android.update(encodedFieldsAndValues.toMap()) + encodedFieldsAndValues.performUpdate(android::update, android::update) }?.await() } - actual suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) { - encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() } - ?.performUpdate { field, value, moreFieldsAndValues -> - android.update(field, value, *moreFieldsAndValues) - }?.await() - } - actual suspend fun delete() { android.delete().await() } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt index 5dc17f72b..7505e09ef 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt @@ -59,7 +59,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.EqualTo -> com.google.firebase.firestore.Filter::equalTo is WhereConstraint.NotEqualTo -> com.google.firebase.firestore.Filter::notEqualTo } - modifier.invoke(field, constraint.safeValue) + modifier.invoke(field, constraint.value) } is WhereConstraint.ForObject -> { val modifier: (String, Any) -> com.google.firebase.firestore.Filter = when (constraint) { @@ -69,7 +69,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.GreaterThanOrEqualTo -> com.google.firebase.firestore.Filter::greaterThanOrEqualTo is WhereConstraint.ArrayContains -> com.google.firebase.firestore.Filter::arrayContains } - modifier.invoke(field, constraint.safeValue) + modifier.invoke(field, constraint.value) } is WhereConstraint.ForArray -> { val modifier: (String, List) -> com.google.firebase.firestore.Filter = when (constraint) { @@ -77,7 +77,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.ArrayContainsAny -> com.google.firebase.firestore.Filter::arrayContainsAny is WhereConstraint.NotInArray -> com.google.firebase.firestore.Filter::notInArray } - modifier.invoke(field, constraint.safeValues) + modifier.invoke(field, constraint.values) } } } @@ -88,7 +88,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.EqualTo -> com.google.firebase.firestore.Filter::equalTo is WhereConstraint.NotEqualTo -> com.google.firebase.firestore.Filter::notEqualTo } - modifier.invoke(path.android, constraint.safeValue) + modifier.invoke(path.android, constraint.value) } is WhereConstraint.ForObject -> { val modifier: (FieldPath, Any) -> com.google.firebase.firestore.Filter = when (constraint) { @@ -98,7 +98,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.GreaterThanOrEqualTo -> com.google.firebase.firestore.Filter::greaterThanOrEqualTo is WhereConstraint.ArrayContains -> com.google.firebase.firestore.Filter::arrayContains } - modifier.invoke(path.android, constraint.safeValue) + modifier.invoke(path.android, constraint.value) } is WhereConstraint.ForArray -> { val modifier: (FieldPath, List) -> com.google.firebase.firestore.Filter = when (constraint) { @@ -106,7 +106,7 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is WhereConstraint.ArrayContainsAny -> com.google.firebase.firestore.Filter::arrayContainsAny is WhereConstraint.NotInArray -> com.google.firebase.firestore.Filter::notInArray } - modifier.invoke(path.android, constraint.safeValues) + modifier.invoke(path.android, constraint.values) } } } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt index 4b8219703..ea20d3f33 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeTransaction import dev.gitlive.firebase.firestore.android import dev.gitlive.firebase.firestore.performUpdate @@ -23,19 +22,17 @@ internal actual class NativeTransactionWrapper internal actual constructor(actua actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject) = native.update(documentRef.android, encodedData.android).let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ) = encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - native.update(documentRef.android, field, value, *moreFieldsAndValues) - }.let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ) = encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - native.update(documentRef.android, field, value, *moreFieldsAndValues) - }.let { this } + encodedFieldsAndValues: List, + ) = encodedFieldsAndValues.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + native.update(documentRef.android, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + native.update(documentRef.android, fieldPath, value, *moreFieldsAndValues) + }, + ).let { this } actual fun delete(documentRef: DocumentReference) = native.delete(documentRef.android).let { this } diff --git a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt index 9aa34f700..1a3bd69b9 100644 --- a/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt +++ b/firebase-firestore/src/androidMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeWriteBatch import dev.gitlive.firebase.firestore.android import dev.gitlive.firebase.firestore.performUpdate @@ -25,19 +24,17 @@ internal actual class NativeWriteBatchWrapper internal actual constructor(actual actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject) = native.update(documentRef.android, encodedData.android).let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ) = encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - native.update(documentRef.android, field, value, *moreFieldsAndValues) - }.let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ) = encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - native.update(documentRef.android, field, value, *moreFieldsAndValues) - }.let { this } + encodedFieldsAndValues: List, + ) = encodedFieldsAndValues.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + native.update(documentRef.android, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + native.update(documentRef.android, fieldPath, value, *moreFieldsAndValues) + }, + ).let { this } actual fun delete(documentRef: DocumentReference) = native.delete(documentRef.android).let { this } diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValueBuilder.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValueBuilder.kt new file mode 100644 index 000000000..53d5a0d13 --- /dev/null +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldValueBuilder.kt @@ -0,0 +1,25 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.EncodeSettings +import kotlinx.serialization.SerializationStrategy + +public class FieldValueBuilder internal constructor() { + + internal val fieldValues: MutableList = mutableListOf() + public var buildSettings: EncodeSettings.Builder.() -> Unit = { + encodeDefaults = true + } + + public inline fun add(value: T) { + addEncoded(encode(value, buildSettings)!!) + } + + public fun addWithStrategy(strategy: SerializationStrategy, value: T) { + addEncoded(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!) + } + + @PublishedApi + internal fun addEncoded(encodedValue: Any) { + fieldValues += encodedValue + } +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldsAndValuesBuilder.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldsAndValuesBuilder.kt new file mode 100644 index 000000000..aba64b228 --- /dev/null +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/FieldsAndValuesBuilder.kt @@ -0,0 +1,39 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.EncodeSettings +import dev.gitlive.firebase.firestore.internal.FieldAndValue +import kotlinx.serialization.SerializationStrategy + +public class FieldsAndValuesBuilder internal constructor() { + + internal val fieldAndValues: MutableList = mutableListOf() + public var buildSettings: EncodeSettings.Builder.() -> Unit = { + encodeDefaults = true + } + + public inline infix fun String.to(value: T) { + toEncoded(encode(value, buildSettings)) + } + + public inline infix fun FieldPath.to(value: T) { + toEncoded(encode(value, buildSettings)) + } + + public fun String.to(strategy: SerializationStrategy, value: T) { + toEncoded(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)) + } + + public fun FieldPath.to(strategy: SerializationStrategy, value: T) { + toEncoded(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)) + } + + @PublishedApi + internal fun String.toEncoded(encodedValue: Any?) { + fieldAndValues += FieldAndValue.WithStringField(this, encodedValue) + } + + @PublishedApi + internal fun FieldPath.toEncoded(encodedValue: Any?) { + fieldAndValues += FieldAndValue.WithFieldPath(this, encodedValue) + } +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/Filter.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/Filter.kt index f9e8558e7..3f5785c9f 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/Filter.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/Filter.kt @@ -1,33 +1,31 @@ package dev.gitlive.firebase.firestore -import dev.gitlive.firebase.firestore.internal.safeValue +import dev.gitlive.firebase.EncodeSettings +import kotlinx.serialization.SerializationStrategy public sealed interface WhereConstraint { public sealed interface ForNullableObject : WhereConstraint { public val value: Any? - public val safeValue: Any? get() = value?.safeValue } public sealed interface ForObject : WhereConstraint { public val value: Any - public val safeValue: Any get() = value.safeValue } public sealed interface ForArray : WhereConstraint { public val values: List - public val safeValues: List get() = values.map { it.safeValue } } - public data class EqualTo internal constructor(override val value: Any?) : ForNullableObject - public data class NotEqualTo internal constructor(override val value: Any?) : ForNullableObject - public data class LessThan internal constructor(override val value: Any) : ForObject - public data class GreaterThan internal constructor(override val value: Any) : ForObject - public data class LessThanOrEqualTo internal constructor(override val value: Any) : ForObject - public data class GreaterThanOrEqualTo internal constructor(override val value: Any) : ForObject - public data class ArrayContains internal constructor(override val value: Any) : ForObject - public data class ArrayContainsAny internal constructor(override val values: List) : ForArray - public data class InArray internal constructor(override val values: List) : ForArray - public data class NotInArray internal constructor(override val values: List) : ForArray + public data class EqualTo @PublishedApi internal constructor(override val value: Any?) : ForNullableObject + public data class NotEqualTo @PublishedApi internal constructor(override val value: Any?) : ForNullableObject + public data class LessThan @PublishedApi internal constructor(override val value: Any) : ForObject + public data class GreaterThan @PublishedApi internal constructor(override val value: Any) : ForObject + public data class LessThanOrEqualTo @PublishedApi internal constructor(override val value: Any) : ForObject + public data class GreaterThanOrEqualTo @PublishedApi internal constructor(override val value: Any) : ForObject + public data class ArrayContains @PublishedApi internal constructor(override val value: Any) : ForObject + public data class ArrayContainsAny @PublishedApi internal constructor(override val values: List) : ForArray + public data class InArray @PublishedApi internal constructor(override val values: List) : ForArray + public data class NotInArray @PublishedApi internal constructor(override val values: List) : ForArray } public sealed class Filter { @@ -37,51 +35,199 @@ public sealed class Filter { public abstract val constraint: WhereConstraint } - public data class Field internal constructor(val field: String, override val constraint: WhereConstraint) : WithConstraint() - public data class Path internal constructor(val path: FieldPath, override val constraint: WhereConstraint) : WithConstraint() + public data class Field @PublishedApi internal constructor(val field: String, override val constraint: WhereConstraint) : WithConstraint() + public data class Path @PublishedApi internal constructor(val path: FieldPath, override val constraint: WhereConstraint) : WithConstraint() } public class FilterBuilder internal constructor() { - public infix fun String.equalTo(value: Any?): Filter.WithConstraint = Filter.Field(this, WhereConstraint.EqualTo(value)) - - public infix fun FieldPath.equalTo(value: Any?): Filter.WithConstraint = Filter.Path(this, WhereConstraint.EqualTo(value)) - - public infix fun String.notEqualTo(value: Any?): Filter.WithConstraint = Filter.Field(this, WhereConstraint.NotEqualTo(value)) - - public infix fun FieldPath.notEqualTo(value: Any?): Filter.WithConstraint = Filter.Path(this, WhereConstraint.NotEqualTo(value)) - - public infix fun String.lessThan(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.LessThan(value)) - - public infix fun FieldPath.lessThan(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.LessThan(value)) - - public infix fun String.greaterThan(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.GreaterThan(value)) - - public infix fun FieldPath.greaterThan(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.GreaterThan(value)) - - public infix fun String.lessThanOrEqualTo(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.LessThanOrEqualTo(value)) - - public infix fun FieldPath.lessThanOrEqualTo(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.LessThanOrEqualTo(value)) - - public infix fun String.greaterThanOrEqualTo(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.GreaterThanOrEqualTo(value)) - - public infix fun FieldPath.greaterThanOrEqualTo(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.GreaterThanOrEqualTo(value)) - - public infix fun String.contains(value: Any): Filter.WithConstraint = Filter.Field(this, WhereConstraint.ArrayContains(value)) - - public infix fun FieldPath.contains(value: Any): Filter.WithConstraint = Filter.Path(this, WhereConstraint.ArrayContains(value)) - - public infix fun String.containsAny(values: List): Filter.WithConstraint = Filter.Field(this, WhereConstraint.ArrayContainsAny(values)) - - public infix fun FieldPath.containsAny(values: List): Filter.WithConstraint = Filter.Path(this, WhereConstraint.ArrayContainsAny(values)) - - public infix fun String.inArray(values: List): Filter.WithConstraint = Filter.Field(this, WhereConstraint.InArray(values)) - - public infix fun FieldPath.inArray(values: List): Filter.WithConstraint = Filter.Path(this, WhereConstraint.InArray(values)) - - public infix fun String.notInArray(values: List): Filter.WithConstraint = Filter.Field(this, WhereConstraint.NotInArray(values)) + public var buildSettings: EncodeSettings.Builder.() -> Unit = { + encodeDefaults = true + } - public infix fun FieldPath.notInArray(values: List): Filter.WithConstraint = Filter.Path(this, WhereConstraint.NotInArray(values)) + public val String.isNull: Filter.WithConstraint get() = Filter.Field(this, WhereConstraint.EqualTo(null)) + public inline infix fun String.equalTo(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.EqualTo(encode(value, buildSettings)), + ) + public fun String.equalTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.EqualTo(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)), + ) + + public val FieldPath.isNull: Filter.WithConstraint get() = Filter.Path(this, WhereConstraint.EqualTo(null)) + public inline infix fun FieldPath.equalTo(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.EqualTo(encode(value, buildSettings)), + ) + public fun FieldPath.equalTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.EqualTo(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)), + ) + + public val String.isNotNull: Filter.WithConstraint get() = Filter.Field(this, WhereConstraint.NotEqualTo(null)) + public inline infix fun String.notEqualTo(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.NotEqualTo(encode(value, buildSettings)), + ) + public fun String.notEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.NotEqualTo(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)), + ) + + public val FieldPath.isNotNull: Filter.WithConstraint get() = Filter.Path(this, WhereConstraint.NotEqualTo(null)) + public inline infix fun FieldPath.notEqualTo(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.NotEqualTo(encode(value, buildSettings)), + ) + public fun FieldPath.notEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.NotEqualTo(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)), + ) + + public inline infix fun String.lessThan(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.LessThan(encode(value, buildSettings)!!), + ) + public fun String.lessThan(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.LessThan(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun FieldPath.lessThan(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.LessThan(encode(value, buildSettings)!!), + ) + public fun FieldPath.lessThan(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.LessThan(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun String.greaterThan(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.GreaterThan(encode(value, buildSettings)!!), + ) + public fun String.greaterThan(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.GreaterThan(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun FieldPath.greaterThan(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.GreaterThan(encode(value, buildSettings)!!), + ) + public fun FieldPath.greaterThan(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.GreaterThan(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun String.lessThanOrEqualTo(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.LessThanOrEqualTo(encode(value, buildSettings)!!), + ) + public fun String.lessThanOrEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.LessThanOrEqualTo(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun FieldPath.lessThanOrEqualTo(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.LessThanOrEqualTo(encode(value, buildSettings)!!), + ) + public fun FieldPath.lessThanOrEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.LessThanOrEqualTo(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun String.greaterThanOrEqualTo(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.GreaterThanOrEqualTo(encode(value, buildSettings)!!), + ) + public fun String.greaterThanOrEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.GreaterThanOrEqualTo(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun FieldPath.greaterThanOrEqualTo(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.GreaterThanOrEqualTo(encode(value, buildSettings)!!), + ) + public fun FieldPath.greaterThanOrEqualTo(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.GreaterThanOrEqualTo(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun String.contains(value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.ArrayContains(encode(value, buildSettings)!!), + ) + public fun String.contains(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.ArrayContains(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun FieldPath.contains(value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.ArrayContains(encode(value, buildSettings)!!), + ) + public fun FieldPath.contains(strategy: SerializationStrategy, value: T): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.ArrayContains(dev.gitlive.firebase.internal.encode(strategy, value, buildSettings)!!), + ) + + public inline infix fun String.containsAny(values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.ArrayContainsAny(values.map { encode(it, buildSettings)!! }), + ) + public fun String.containsAny(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.ArrayContainsAny(values.map { dev.gitlive.firebase.internal.encode(strategy, it, buildSettings)!! }), + ) + + public inline infix fun FieldPath.containsAny(values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.ArrayContainsAny(values.map { encode(it, buildSettings)!! }), + ) + public fun FieldPath.containsAny(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.ArrayContainsAny(values.map { dev.gitlive.firebase.internal.encode(strategy, it, buildSettings)!! }), + ) + + public inline infix fun String.inArray(values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.InArray(values.map { encode(it, buildSettings)!! }), + ) + public fun String.inArray(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.InArray(values.map { dev.gitlive.firebase.internal.encode(strategy, it, buildSettings)!! }), + ) + + public inline infix fun FieldPath.inArray(values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.InArray(values.map { encode(it, buildSettings)!! }), + ) + public fun FieldPath.inArray(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.InArray(values.map { dev.gitlive.firebase.internal.encode(strategy, it, buildSettings)!! }), + ) + + public inline infix fun String.notInArray(values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.NotInArray(values.map { encode(it, buildSettings)!! }), + ) + public fun String.notInArray(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Field( + this, + WhereConstraint.NotInArray(values.map { dev.gitlive.firebase.internal.encode(strategy, it, buildSettings)!! }), + ) + + public inline infix fun FieldPath.notInArray(values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.NotInArray(values.map { encode(it, buildSettings)!! }), + ) + public fun FieldPath.notInArray(strategy: SerializationStrategy, values: List): Filter.WithConstraint = Filter.Path( + this, + WhereConstraint.NotInArray(values.map { dev.gitlive.firebase.internal.encode(strategy, it, buildSettings)!! }), + ) public infix fun Filter.and(right: Filter): Filter.And { val leftList = when (this) { diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt index bf4723e49..c87f4194c 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -18,7 +18,6 @@ import dev.gitlive.firebase.firestore.internal.NativeQueryWrapper import dev.gitlive.firebase.firestore.internal.NativeTransactionWrapper import dev.gitlive.firebase.firestore.internal.NativeWriteBatchWrapper import dev.gitlive.firebase.firestore.internal.SetOptions -import dev.gitlive.firebase.firestore.internal.safeValue import dev.gitlive.firebase.internal.decode import dev.gitlive.firebase.internal.encodeAsObject import kotlinx.coroutines.flow.Flow @@ -179,31 +178,56 @@ public data class Transaction internal constructor(internal val nativeWrapper: N internal fun setEncoded(documentRef: DocumentReference, encodedData: EncodedObject, setOptions: SetOptions): Transaction = Transaction(nativeWrapper.setEncoded(documentRef, encodedData, setOptions)) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("update(documentRef, data) { this.encodeDefaults = encodeDefaults }")) - public fun update(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean): Transaction = update(documentRef, data) { - this.encodeDefaults = encodeDefaults - } + public fun update(documentRef: DocumentReference, data: Any, encodeDefaults: Boolean): Transaction = + update(documentRef, data) { + this.encodeDefaults = encodeDefaults + } public inline fun update(documentRef: DocumentReference, data: Any, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Transaction = updateEncoded(documentRef, encodeAsObject(data, buildSettings)) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("update(documentRef, strategy, data) { this.encodeDefaults = encodeDefaults }")) - public fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean): Transaction = update(documentRef, strategy, data) { - this.encodeDefaults = encodeDefaults - } + public fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean): Transaction = + update(documentRef, strategy, data) { + this.encodeDefaults = encodeDefaults + } public inline fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Transaction = updateEncoded(documentRef, encodeAsObject(strategy, data, buildSettings)) @JvmName("updateFields") - public inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Transaction = updateEncodedFieldsAndValues(documentRef, encodeFieldAndValue(fieldsAndValues, buildSettings).orEmpty()) + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair): Transaction = + update(documentRef, {}, *fieldsAndValues) + + @JvmName("updateFields") + public fun update(documentRef: DocumentReference, buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldsAndValues: Pair): Transaction = + update( + documentRef, + ) { + this.buildSettings = buildSettings + fieldsAndValues.forEach { (field, value) -> + field to value + } + } @JvmName("updateFieldPaths") - public inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}): Transaction = updateEncodedFieldPathsAndValues(documentRef, encodeFieldAndValue(fieldsAndValues, buildSettings).orEmpty()) + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair): Transaction = + update(documentRef, {}, *fieldsAndValues) - @PublishedApi - internal fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): Transaction = Transaction(nativeWrapper.updateEncoded(documentRef, encodedData)) + @JvmName("updateFieldPaths") + public fun update(documentRef: DocumentReference, buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldsAndValues: Pair): Transaction = + update( + documentRef, + ) { + this.buildSettings = buildSettings + fieldsAndValues.forEach { (field, value) -> + field to value + } + } - @PublishedApi - internal fun updateEncodedFieldsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): Transaction = Transaction(nativeWrapper.updateEncodedFieldsAndValues(documentRef, encodedFieldsAndValues)) + public fun update( + documentRef: DocumentReference, + fieldsAndValuesBuilder: FieldsAndValuesBuilder.() -> Unit, + ): Transaction = Transaction(nativeWrapper.updateEncoded(documentRef, FieldsAndValuesBuilder().apply(fieldsAndValuesBuilder).fieldAndValues)) @PublishedApi - internal fun updateEncodedFieldPathsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): Transaction = Transaction(nativeWrapper.updateEncodedFieldPathsAndValues(documentRef, encodedFieldsAndValues)) + internal fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): Transaction = Transaction(nativeWrapper.updateEncoded(documentRef, encodedData)) public fun delete(documentRef: DocumentReference): Transaction = Transaction(nativeWrapper.delete(documentRef)) public suspend fun get(documentRef: DocumentReference): DocumentSnapshot = DocumentSnapshot(nativeWrapper.get(documentRef)) @@ -230,14 +254,52 @@ public open class Query internal constructor(internal val nativeQuery: NativeQue public fun orderBy(field: FieldPath, direction: Direction = Direction.ASCENDING): Query = Query(nativeQuery.orderBy(field.encoded, direction)) public fun startAfter(document: DocumentSnapshot): Query = Query(nativeQuery.startAfter(document.native)) - public fun startAfter(vararg fieldValues: Any): Query = Query(nativeQuery.startAfter(*(fieldValues.map { it.safeValue }.toTypedArray()))) + public fun startAfter(vararg fieldValues: Any?): Query = startAfter({}, *fieldValues) + public fun startAfter(buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldValues: Any?): Query = + startAfter { + this.buildSettings = buildSettings + fieldValues.forEach { + add(it) + } + } + + public fun startAfter(builder: FieldValueBuilder.() -> Unit): Query = Query(nativeQuery.startAfter(*FieldValueBuilder().apply(builder).fieldValues.toTypedArray())) + public fun startAt(document: DocumentSnapshot): Query = Query(nativeQuery.startAt(document.native)) - public fun startAt(vararg fieldValues: Any): Query = Query(nativeQuery.startAt(*(fieldValues.map { it.safeValue }.toTypedArray()))) + public fun startAt(vararg fieldValues: Any?): Query = startAt({}, *fieldValues) + public fun startAt(buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldValues: Any?): Query = + startAt { + this.buildSettings = buildSettings + fieldValues.forEach { + add(it) + } + } + + public fun startAt(builder: FieldValueBuilder.() -> Unit): Query = Query(nativeQuery.startAt(*FieldValueBuilder().apply(builder).fieldValues.toTypedArray())) public fun endBefore(document: DocumentSnapshot): Query = Query(nativeQuery.endBefore(document.native)) - public fun endBefore(vararg fieldValues: Any): Query = Query(nativeQuery.endBefore(*(fieldValues.map { it.safeValue }.toTypedArray()))) + public fun endBefore(vararg fieldValues: Any?): Query = endBefore({}, *fieldValues) + public fun endBefore(buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldValues: Any?): Query = + endBefore { + this.buildSettings = buildSettings + fieldValues.forEach { + add(it) + } + } + + public fun endBefore(builder: FieldValueBuilder.() -> Unit): Query = Query(nativeQuery.endBefore(*FieldValueBuilder().apply(builder).fieldValues.toTypedArray())) + public fun endAt(document: DocumentSnapshot): Query = Query(nativeQuery.endAt(document.native)) - public fun endAt(vararg fieldValues: Any): Query = Query(nativeQuery.endAt(*(fieldValues.map { it.safeValue }.toTypedArray()))) + public fun endAt(vararg fieldValues: Any?): Query = endAt({}, *fieldValues) + public fun endAt(buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldValues: Any?): Query = + endAt { + this.buildSettings = buildSettings + fieldValues.forEach { + add(it) + } + } + + public fun endAt(builder: FieldValueBuilder.() -> Unit): Query = Query(nativeQuery.endAt(*FieldValueBuilder().apply(builder).fieldValues.toTypedArray())) } @Deprecated("Deprecated in favor of using a [FilterBuilder]", replaceWith = ReplaceWith("where { field equalTo equalTo }", "dev.gitlive.firebase.firestore")) @@ -348,33 +410,63 @@ public data class WriteBatch internal constructor(internal val nativeWrapper: Na internal fun setEncoded(documentRef: DocumentReference, encodedData: EncodedObject, setOptions: SetOptions): WriteBatch = WriteBatch(nativeWrapper.setEncoded(documentRef, encodedData, setOptions)) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("update(documentRef, data) { this.encodeDefaults = encodeDefaults }")) - public inline fun update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean): WriteBatch = update(documentRef, data) { - this.encodeDefaults = encodeDefaults - } + public inline fun update(documentRef: DocumentReference, data: T, encodeDefaults: Boolean): WriteBatch = + update(documentRef, data) { + this.encodeDefaults = encodeDefaults + } public inline fun update(documentRef: DocumentReference, data: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): WriteBatch = updateEncoded(documentRef, encodeAsObject(data, buildSettings)) @Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("update(documentRef, strategy, data) { this.encodeDefaults = encodeDefaults }")) - public fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean): WriteBatch = update(documentRef, strategy, data) { - this.encodeDefaults = encodeDefaults - } + public fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, encodeDefaults: Boolean): WriteBatch = + update(documentRef, strategy, data) { + this.encodeDefaults = encodeDefaults + } public inline fun update(documentRef: DocumentReference, strategy: SerializationStrategy, data: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}): WriteBatch = updateEncoded(documentRef, encodeAsObject(strategy, data, buildSettings)) @JvmName("updateField") - public inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}): WriteBatch = updateEncodedFieldsAndValues(documentRef, encodeFieldAndValue(fieldsAndValues, buildSettings).orEmpty()) + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair): WriteBatch = + update(documentRef, {}, *fieldsAndValues) + + @JvmName("updateField") + public fun update(documentRef: DocumentReference, buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldsAndValues: Pair): WriteBatch = + update( + documentRef, + ) { + this.buildSettings = buildSettings + fieldsAndValues.forEach { (field, value) -> + field to value + } + } @JvmName("updateFieldPath") - public inline fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}): WriteBatch = updateEncodedFieldPathsAndValues(documentRef, encodeFieldAndValue(fieldsAndValues, buildSettings).orEmpty()) + public fun update(documentRef: DocumentReference, vararg fieldsAndValues: Pair): WriteBatch = + update(documentRef, {}, *fieldsAndValues) - @PublishedApi - internal fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): WriteBatch = WriteBatch(nativeWrapper.updateEncoded(documentRef, encodedData)) + @JvmName("updateFieldPath") + public fun update(documentRef: DocumentReference, buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldsAndValues: Pair): WriteBatch = + update( + documentRef, + ) { + this.buildSettings = buildSettings + fieldsAndValues.forEach { (path, value) -> + path to value + } + } - @PublishedApi - internal fun updateEncodedFieldsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): WriteBatch = WriteBatch(nativeWrapper.updateEncodedFieldsAndValues(documentRef, encodedFieldsAndValues)) + public fun update( + documentRef: DocumentReference, + fieldAndValuesBuilder: FieldsAndValuesBuilder.() -> Unit, + ): WriteBatch = WriteBatch( + nativeWrapper.updateEncoded( + documentRef, + FieldsAndValuesBuilder().apply(fieldAndValuesBuilder).fieldAndValues, + ), + ) @PublishedApi - internal fun updateEncodedFieldPathsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): WriteBatch = WriteBatch(nativeWrapper.updateEncodedFieldPathsAndValues(documentRef, encodedFieldsAndValues)) + internal fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): WriteBatch = WriteBatch(nativeWrapper.updateEncoded(documentRef, encodedData)) public fun delete(documentRef: DocumentReference): WriteBatch = WriteBatch(nativeWrapper.delete(documentRef)) public suspend fun commit() { @@ -513,33 +605,35 @@ public data class DocumentReference internal constructor(internal val native: Na } @JvmName("updateFields") - public suspend inline fun update(vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}) { - updateEncodedFieldsAndValues( - encodeFieldAndValue( - fieldsAndValues, - buildSettings, - ).orEmpty(), - ) - } + public suspend fun update(vararg fieldsAndValues: Pair): Unit = + update({}, *fieldsAndValues) - @PublishedApi - internal suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) { - native.updateEncodedFieldsAndValues(encodedFieldsAndValues) - } + @JvmName("updateFields") + public suspend fun update(buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldsAndValues: Pair): Unit = + update { + this.buildSettings = buildSettings + fieldsAndValues.forEach { (field, value) -> + field to value + } + } @JvmName("updateFieldPaths") - public suspend inline fun update(vararg fieldsAndValues: Pair, buildSettings: EncodeSettings.Builder.() -> Unit = {}) { - updateEncodedFieldPathsAndValues( - encodeFieldAndValue( - fieldsAndValues, - buildSettings, - ).orEmpty(), - ) - } + public suspend fun update(vararg fieldsAndValues: Pair): Unit = + update({}, *fieldsAndValues) - @PublishedApi - internal suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) { - native.updateEncodedFieldPathsAndValues(encodedFieldsAndValues) + @JvmName("updateFieldPaths") + public suspend fun update(buildSettings: EncodeSettings.Builder.() -> Unit, vararg fieldsAndValues: Pair): Unit = + update { + this.buildSettings = buildSettings + fieldsAndValues.forEach { (fieldPath, value) -> + fieldPath to value + } + } + + public suspend fun update( + fieldsAndValuesBuilder: FieldsAndValuesBuilder.() -> Unit, + ) { + native.updateEncoded(FieldsAndValuesBuilder().apply(fieldsAndValuesBuilder).fieldAndValues) } public suspend fun delete() { diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/helpers.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/helpers.kt index 766836a76..7247ca002 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/helpers.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/helpers.kt @@ -1,38 +1,39 @@ package dev.gitlive.firebase.firestore -import dev.gitlive.firebase.EncodeSettings -import kotlin.jvm.JvmName +import dev.gitlive.firebase.firestore.internal.FieldAndValue -// ** Helper method to perform an update operation. */ -@JvmName("performUpdateFields") -@PublishedApi -internal inline fun encodeFieldAndValue( - fieldsAndValues: Array>, - buildSettings: EncodeSettings.Builder.() -> Unit, -): List>? = encodeFieldAndValue(fieldsAndValues, encodeField = { it }, encodeValue = { encode(it, buildSettings) }) +internal fun List.performUpdate( + updateAsField: (String, Any?, Array) -> R, + updateAsFieldPath: (EncodedFieldPath, Any?, Array) -> R, +): R { + val first = first() + val remaining = drop(1).flatMap { fieldAndValue -> + listOf( + when (fieldAndValue) { + is FieldAndValue.WithFieldPath -> fieldAndValue.path.encoded + is FieldAndValue.WithStringField -> fieldAndValue.field + }, + fieldAndValue.value, + ) + } + return when (first) { + is FieldAndValue.WithFieldPath -> updateAsFieldPath( + first.path.encoded, + first.value, + remaining.toTypedArray(), + ) -/** Helper method to perform an update operation. */ -@JvmName("performUpdateFieldPaths") -@PublishedApi -internal inline fun encodeFieldAndValue( - fieldsAndValues: Array>, - buildSettings: EncodeSettings.Builder.() -> Unit, -): List>? = encodeFieldAndValue(fieldsAndValues, { it.encoded }, { encode(it, buildSettings) }) + is FieldAndValue.WithStringField -> updateAsField( + first.field, + first.value, + remaining.toTypedArray(), + ) + } +} -/** Helper method to perform an update operation in Android and JS. */ -@PublishedApi -internal inline fun encodeFieldAndValue( - fieldsAndValues: Array>, - encodeField: (T) -> K, - encodeValue: (Any?) -> Any?, -): List>? = - fieldsAndValues.takeUnless { fieldsAndValues.isEmpty() } - ?.map { (field, value) -> encodeField(field) to value?.let { encodeValue(it) } } - -internal fun List>.performUpdate( - update: (K, Any?, Array) -> R, -) = update( - this[0].first, - this[0].second, - this.drop(1).flatMap { (field, value) -> listOf(field, value) }.toTypedArray(), -) +internal fun List.toEncodedMap(): Map = associate { fieldAndValue -> + when (fieldAndValue) { + is FieldAndValue.WithStringField -> fieldAndValue.field to fieldAndValue.value + is FieldAndValue.WithFieldPath -> fieldAndValue.path.encoded to fieldAndValue.value + } +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/FieldAndValue.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/FieldAndValue.kt new file mode 100644 index 000000000..c4d5beeb4 --- /dev/null +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/FieldAndValue.kt @@ -0,0 +1,11 @@ +package dev.gitlive.firebase.firestore.internal + +import dev.gitlive.firebase.firestore.FieldPath + +internal sealed class FieldAndValue { + + abstract val value: Any? + + data class WithStringField(val field: String, override val value: Any?) : FieldAndValue() + data class WithFieldPath(val path: FieldPath, override val value: Any?) : FieldAndValue() +} diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index baa706419..7cf376cf8 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -1,6 +1,5 @@ package dev.gitlive.firebase.firestore.internal -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeCollectionReference import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot @@ -20,7 +19,6 @@ internal expect class NativeDocumentReference(nativeValue: NativeDocumentReferen suspend fun get(source: Source = Source.DEFAULT): NativeDocumentSnapshot suspend fun setEncoded(encodedData: EncodedObject, setOptions: SetOptions) suspend fun updateEncoded(encodedData: EncodedObject) - suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) - suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) + suspend fun updateEncoded(encodedFieldsAndValues: List) suspend fun delete() } diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt index 6bb72eedf..8a4df4971 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeTransaction import dev.gitlive.firebase.internal.EncodedObject @@ -11,8 +10,7 @@ internal expect class NativeTransactionWrapper internal constructor(native: Nati fun setEncoded(documentRef: DocumentReference, encodedData: EncodedObject, setOptions: SetOptions): NativeTransactionWrapper fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeTransactionWrapper - fun updateEncodedFieldsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): NativeTransactionWrapper - fun updateEncodedFieldPathsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): NativeTransactionWrapper + fun updateEncoded(documentRef: DocumentReference, encodedFieldsAndValues: List): NativeTransactionWrapper fun delete(documentRef: DocumentReference): NativeTransactionWrapper suspend fun get(documentRef: DocumentReference): NativeDocumentSnapshotWrapper } diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt index bea3d0071..fee11b836 100644 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt +++ b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeWriteBatch import dev.gitlive.firebase.internal.EncodedObject @@ -9,8 +8,7 @@ internal expect class NativeWriteBatchWrapper internal constructor(native: Nativ val native: NativeWriteBatch fun setEncoded(documentRef: DocumentReference, encodedData: EncodedObject, setOptions: SetOptions): NativeWriteBatchWrapper fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeWriteBatchWrapper - fun updateEncodedFieldsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): NativeWriteBatchWrapper - fun updateEncodedFieldPathsAndValues(documentRef: DocumentReference, encodedFieldsAndValues: List>): NativeWriteBatchWrapper + fun updateEncoded(documentRef: DocumentReference, encodedFieldsAndValues: List): NativeWriteBatchWrapper fun delete(documentRef: DocumentReference): NativeWriteBatchWrapper suspend fun commit() } diff --git a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/SafeValue.kt b/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/SafeValue.kt deleted file mode 100644 index afca32060..000000000 --- a/firebase-firestore/src/commonMain/kotlin/dev/gitlive/firebase/firestore/internal/SafeValue.kt +++ /dev/null @@ -1,14 +0,0 @@ -package dev.gitlive.firebase.firestore.internal - -import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.GeoPoint -import dev.gitlive.firebase.firestore.Timestamp - -internal val Any.safeValue: Any get() = when (this) { - is Timestamp -> nativeValue - is GeoPoint -> nativeValue - is DocumentReference -> native.nativeValue - is Map<*, *> -> this.mapNotNull { (key, value) -> key?.let { it.safeValue to value?.safeValue } } - is Collection<*> -> this.mapNotNull { it?.safeValue } - else -> this -} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt new file mode 100644 index 000000000..d58024788 --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/DocumentReferenceTest.kt @@ -0,0 +1,430 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.internal.decode +import dev.gitlive.firebase.runTest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withContext +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.builtins.nullable +import kotlinx.serialization.builtins.serializer +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds + +@IgnoreForAndroidUnitTest +class DocumentReferenceTest : BaseFirebaseFirestoreTest() { + + @Serializable + data class FirestoreTimeTest( + val prop1: String, + val time: BaseTimestamp?, + ) + + @Serializable + data class TestDataWithDocumentReference( + val uid: String, + val reference: DocumentReference, + val optionalReference: DocumentReference?, + ) + + @Serializable + data class TestDataWithOptionalDocumentReference( + val optionalReference: DocumentReference?, + ) + + @Test + fun encodeDocumentReference() = runTest { + val doc = firestore.document("a/b") + val item = TestDataWithDocumentReference("123", doc, doc) + val encoded = encodedAsMap( + encode(item) { + encodeDefaults = false + }, + ) + assertEquals("123", encoded["uid"]) + assertEquals(doc.nativeValue, encoded["reference"]) + assertEquals(doc.nativeValue, encoded["optionalReference"]) + } + + @Test + fun encodeNullDocumentReference() = runTest { + val item = TestDataWithOptionalDocumentReference(null) + val encoded = encodedAsMap( + encode(item) { + encodeDefaults = false + }, + ) + assertNull(encoded["optionalReference"]) + } + + @Test + fun decodeDocumentReference() = runTest { + val doc = firestore.document("a/b") + val obj = mapOf( + "uid" to "123", + "reference" to doc.nativeValue, + "optionalReference" to doc.nativeValue, + ).asEncoded() + val decoded: TestDataWithDocumentReference = decode(obj) + assertEquals("123", decoded.uid) + assertEquals(doc.path, decoded.reference.path) + assertEquals(doc.path, decoded.optionalReference?.path) + } + + @Test + fun decodeNullDocumentReference() = runTest { + val obj = mapOf("optionalReference" to null).asEncoded() + val decoded: TestDataWithOptionalDocumentReference = decode(obj) + assertNull(decoded.optionalReference?.path) + } + + @Test + fun testServerTimestampFieldValue() = testDocument( + firestore + .collection("testServerTimestampFieldValue") + .document("test"), + ) { doc -> + doc.set( + FirestoreTimeTest.serializer(), + FirestoreTimeTest("ServerTimestamp", Timestamp(123, 0)), + ) + assertEquals(Timestamp(123, 0), doc.get().get("time", TimestampSerializer)) + + doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestamp", Timestamp.ServerTimestamp)) + + assertNotEquals(Timestamp.ServerTimestamp, doc.get().get("time", BaseTimestamp.serializer())) + assertNotEquals(Timestamp.ServerTimestamp, doc.get().data(FirestoreTimeTest.serializer()).time) + } + + @Test + fun testServerTimestampBehaviorNone() = testDocument( + firestore + .collection("testServerTimestampBehaviorNone") + .document("test${Random.nextInt()}"), + ) { doc -> + val deferredPendingWritesSnapshot = async { + doc.snapshots.filter { it.exists }.first() + } + nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot + + doc.set( + FirestoreTimeTest.serializer(), + FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp), + ) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNull(pendingWritesSnapshot.get("time", BaseTimestamp.serializer().nullable, serverTimestampBehavior = ServerTimestampBehavior.NONE)) + } + + @Test + fun testServerTimestampBehaviorEstimate() = testDocument( + firestore + .collection("testServerTimestampBehaviorEstimate") + .document("test${Random.nextInt()}"), + ) { doc -> + val deferredPendingWritesSnapshot = async { + doc.snapshots.filter { it.exists }.first() + } + nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot + + doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp)) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNotNull(pendingWritesSnapshot.get("time", ServerTimestampBehavior.ESTIMATE)) + assertNotEquals(Timestamp.ServerTimestamp, pendingWritesSnapshot.data(FirestoreTimeTest.serializer(), serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE).time) + } + + @Test + fun testServerTimestampBehaviorPrevious() = testDocument( + firestore + .collection("testServerTimestampBehaviorPrevious") + .document("test${Random.nextInt()}"), + ) { doc -> + val deferredPendingWritesSnapshot = async { + doc.snapshots.filter { it.exists }.first() + } + nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot + + doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp)) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNull(pendingWritesSnapshot.get("time", BaseTimestamp.serializer().nullable, serverTimestampBehavior = ServerTimestampBehavior.PREVIOUS)) + } + + @Test + fun testDocumentAutoId() = testDocument( + firestore + .collection("testDocumentAutoId") + .document, + ) { doc -> + + doc.set(FirestoreTest.serializer(), FirestoreTest("AutoId")) + + val resultDoc = firestore + .collection("testDocumentAutoId") + .document(doc.id) + .get() + + assertEquals(true, resultDoc.exists) + assertEquals("AutoId", resultDoc.get("prop1")) + } + + @Test + fun testUpdateValues() = testDocument( + firestore + .collection("testFirestoreUpdateMultipleValues") + .document("test1"), + ) { doc -> + doc.set( + FirestoreTest.serializer(), + FirestoreTest( + "property", + count = 0, + nested = NestedObject("nested"), + duration = 600.milliseconds, + ), + ) + val dataBefore = doc.get().data(FirestoreTest.serializer()) + assertEquals(0, dataBefore.count) + assertNull(dataBefore.optional) + assertEquals(NestedObject("nested"), dataBefore.nested) + assertEquals(600.milliseconds, dataBefore.duration) + + doc.update { + FirestoreTest::count.name to 5 + FieldPath(FirestoreTest::optional.name) to "notNull" + FirestoreTest::nested.name.to( + NestedObject.serializer(), + NestedObject("newProperty"), + ) + FieldPath(FirestoreTest::duration.name).to( + DurationAsIntSerializer(), + 700.milliseconds, + ) + } + val dataAfter = doc.get().data(FirestoreTest.serializer()) + assertEquals(5, dataAfter.count) + assertEquals("notNull", dataAfter.optional) + assertEquals(NestedObject("newProperty"), dataAfter.nested) + assertEquals(700.milliseconds, dataAfter.duration) + } + + @Test + fun testIncrementFieldValue() = testDocument( + firestore + .collection("testFirestoreIncrementFieldValue") + .document("test1"), + ) { doc -> + doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", count = 0)) + val dataBefore = doc.get().data(FirestoreTest.serializer()) + assertEquals(0, dataBefore.count) + + doc.update("count" to FieldValue.increment(5)) + val dataAfter = doc.get().data(FirestoreTest.serializer()) + assertEquals(5, dataAfter.count) + } + + @Test + fun testArrayUnion() = testDocument( + firestore + .collection("testFirestoreArrayUnion") + .document("test1"), + ) { doc -> + doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first"))) + val dataBefore = doc.get().data(FirestoreTest.serializer()) + assertEquals(listOf("first"), dataBefore.list) + + doc.update("list" to FieldValue.arrayUnion("second")) + val dataAfter = doc.get().data(FirestoreTest.serializer()) + assertEquals(listOf("first", "second"), dataAfter.list) + } + + @Test + fun testArrayRemove() = testDocument( + firestore + .collection("testFirestoreArrayRemove") + .document("test1"), + ) { doc -> + doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first", "second"))) + val dataBefore = doc.get().data(FirestoreTest.serializer()) + assertEquals(listOf("first", "second"), dataBefore.list) + + doc.update("list" to FieldValue.arrayRemove("second")) + val dataAfter = doc.get().data(FirestoreTest.serializer()) + assertEquals(listOf("first"), dataAfter.list) + } + + @Test + fun testLegacyDoubleTimestamp() = testDocument( + firestore + .collection("testLegacyDoubleTimestamp") + .document("test${Random.nextInt()}"), + ) { doc -> + @Serializable + data class DoubleTimestamp( + @Serializable(with = DoubleAsTimestampSerializer::class) + val time: Double?, + ) + + val deferredPendingWritesSnapshot = async { + doc.snapshots.filter { it.exists }.first() + } + nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot + + doc.set(DoubleTimestamp.serializer(), DoubleTimestamp(DoubleAsTimestampSerializer.SERVER_TIMESTAMP)) + + val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() + assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) + assertNotNull(pendingWritesSnapshot.get("time", DoubleAsTimestampSerializer, serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE)) + assertNotEquals(DoubleAsTimestampSerializer.SERVER_TIMESTAMP, pendingWritesSnapshot.data(DoubleTimestamp.serializer(), serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE).time) + } + + @Test + fun testLegacyDoubleTimestampWriteNewFormatRead() = testDocument( + firestore + .collection("testLegacyDoubleTimestampEncodeDecode") + .document("testLegacy"), + ) { doc -> + @Serializable + data class LegacyDocument( + @Serializable(with = DoubleAsTimestampSerializer::class) + val time: Double, + ) + + @Serializable + data class NewDocument( + val time: Timestamp, + ) + + val ms = 12345678.0 + + doc.set(LegacyDocument.serializer(), LegacyDocument(time = ms)) + + val fetched: NewDocument = doc.get().data(NewDocument.serializer()) + assertEquals(ms, fetched.time.toMilliseconds()) + } + + @Test + fun testGeoPointSerialization() = testDocument( + firestore.collection("geoPointSerialization") + .document("geoPointSerialization"), + ) { doc -> + @Serializable + data class DataWithGeoPoint(val geoPoint: GeoPoint) + + val data = DataWithGeoPoint(GeoPoint(12.34, 56.78)) + // store geo point + doc.set(DataWithGeoPoint.serializer(), data) + // restore data + val savedData = doc.get().data(DataWithGeoPoint.serializer()) + assertEquals(data.geoPoint, savedData.geoPoint) + + // update data + val updatedData = DataWithGeoPoint(GeoPoint(87.65, 43.21)) + doc.update(FieldPath(DataWithGeoPoint::geoPoint.name) to updatedData.geoPoint) + // verify update + val updatedSavedData = doc.get().data(DataWithGeoPoint.serializer()) + assertEquals(updatedData.geoPoint, updatedSavedData.geoPoint) + } + + @Test + fun testDocumentReferenceSerialization() = runTest { + @Serializable + data class DataWithDocumentReference( + val documentReference: DocumentReference, + ) + + val collection = firestore.collection("documentReferenceSerialization") + val document = collection + .document("documentReferenceSerialization") + val documentRef1 = collection.document("refDoc1") + val documentRef2 = collection.document("refDoc2") + + try { + documentRef1.set(mapOf("value" to 1)) + documentRef2.set(mapOf("value" to 2)) + val data = DataWithDocumentReference(documentRef1) + // store reference + document.set(DataWithDocumentReference.serializer(), data) + // restore data + val savedData = document.get().data(DataWithDocumentReference.serializer()) + assertEquals(data.documentReference.path, savedData.documentReference.path) + + // update data + val updatedData = DataWithDocumentReference(documentRef2) + document.update { + FieldPath(DataWithDocumentReference::documentReference.name).to( + DocumentReferenceSerializer, + updatedData.documentReference, + ) + } + // verify update + val updatedSavedData = document.get().data(DataWithDocumentReference.serializer()) + assertEquals( + updatedData.documentReference.path, + updatedSavedData.documentReference.path, + ) + } finally { + document.delete() + documentRef1.delete() + documentRef2.delete() + } + } + + @Test + fun testFieldValuesOps() = testDocument( + firestore.collection("fieldValuesOps") + .document("fieldValuesOps"), + ) { doc -> + @Serializable + data class TestData(val values: List) + + val data = TestData(listOf(1)) + // store + doc.set(TestData.serializer(), data) + // append & verify + doc.update(FieldPath(TestData::values.name) to FieldValue.arrayUnion(2)) + + var savedData = doc.get().data(TestData.serializer()) + assertEquals(listOf(1, 2), savedData.values) + + // remove & verify + doc.update(FieldPath(TestData::values.name) to FieldValue.arrayRemove(1)) + savedData = doc.get().data(TestData.serializer()) + assertEquals(listOf(2), savedData.values) + + val list = doc.get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) + assertEquals(listOf(2), list) + // delete & verify + doc.update(FieldPath(TestData::values.name) to FieldValue.delete) + val deletedList = doc.get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) + assertNull(deletedList) + } + + private suspend fun nonSkippedDelay(timeout: Duration) = withContext(Dispatchers.Default) { + delay(timeout) + } + + private fun testDocument(document: DocumentReference, block: suspend CoroutineScope.(DocumentReference) -> Unit) = runTest { + try { + block(document) + } finally { + document.delete() + } + } +} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt index 2a5d74d90..f87664321 100644 --- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/FirestoreSourceTest.kt @@ -12,7 +12,7 @@ class FirestoreSourceTest { lateinit var firestore: FirebaseFirestore companion object { - val testDoc = FirebaseFirestoreTest.FirestoreTest( + val testDoc = BaseFirebaseFirestoreTest.FirestoreTest( "aaa", 0.0, 1, @@ -21,10 +21,6 @@ class FirestoreSourceTest { ) } - private suspend fun setDoc() { - firestore.collection("testFirestoreQuerying").document("one").set(testDoc) - } - private fun initializeFirebase(persistenceEnabled: Boolean = false) { val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize( context, @@ -58,67 +54,75 @@ class FirestoreSourceTest { } @Test - fun testGetFromServer_withPersistence() = runTest { - initializeFirebase(persistenceEnabled = true) - setDoc() - val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.SERVER) + fun testGetFromServer_withPersistence() = testFirebaseDoc(true) { + val doc = get(Source.SERVER) assertTrue(doc.exists) assertFalse(doc.metadata.isFromCache) } @Test - fun testGetFromServer_withoutPersistence() = runTest { - initializeFirebase(persistenceEnabled = false) - setDoc() - val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.SERVER) + fun testGetFromServer_withoutPersistence() = testFirebaseDoc(false) { + val doc = get(Source.SERVER) assertTrue(doc.exists) assertFalse(doc.metadata.isFromCache) } @Test - fun testGetFromCache() = runTest { - initializeFirebase(persistenceEnabled = true) - + fun testGetFromCache() = testFirebaseDoc(true) { // Warm up cache by setting a document - setDoc() + set(testDoc) - val cachedDoc = firestore.collection("testFirestoreQuerying").document("one").get(Source.CACHE) + val cachedDoc = get(Source.CACHE) assertTrue(cachedDoc.exists) assertTrue(cachedDoc.metadata.isFromCache) } @Test - fun testGetFromCache_withoutPersistence() = runTest { - initializeFirebase(persistenceEnabled = false) - setDoc() + fun testGetFromCache_withoutPersistence() = testFirebaseDoc(false) { assertFailsWith(FirebaseFirestoreException::class) { - firestore.collection("testFirestoreQuerying").document("one").get(Source.CACHE) + get(Source.CACHE) } } @Test - fun testGetDefault_withPersistence() = runTest { - initializeFirebase(persistenceEnabled = false) - val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.DEFAULT) + fun testGetDefault_withPersistence() = testFirebaseDoc(false) { + val doc = get(Source.DEFAULT) assertTrue(doc.exists) assertFalse(doc.metadata.isFromCache) } @Test - fun testGet() = runTest { - initializeFirebase(persistenceEnabled = false) - val doc = firestore.collection("testFirestoreQuerying").document("one").get() + fun testGet() = testFirebaseDoc(false) { + val doc = get() assertTrue(doc.exists) assertFalse(doc.metadata.isFromCache) } @Test - fun testGetDefault_withoutPersistence() = runTest { - initializeFirebase(persistenceEnabled = true) - setDoc() - val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.DEFAULT) + fun testGetDefault_withoutPersistence() = testFirebaseDoc(true) { + val doc = get(Source.DEFAULT) assertTrue(doc.exists) // Firebase defaults to first fetching from server assertFalse(doc.metadata.isFromCache) } + + private fun testFirebaseDoc( + persistenceEnabled: Boolean, + block: suspend DocumentReference.() -> Unit, + ) = runTest { + initializeFirebase() + val doc = firestore.collection("testFirestoreQuerying").document("one") + doc.set(testDoc) + + Firebase.apps(context).forEach { it.delete() } + + initializeFirebase(persistenceEnabled = persistenceEnabled) + + val newDoc = firestore.collection("testFirestoreQuerying").document("one") + try { + newDoc.block() + } finally { + newDoc.delete() + } + } } diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt new file mode 100644 index 000000000..665044bda --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/QueryTest.kt @@ -0,0 +1,662 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.runTest +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.time.Duration.Companion.milliseconds + +open class QueryTest : BaseFirebaseFirestoreTest() { + + companion object { + const val COLLECTION = "testFirestoreQuerying" + val testOne = FirestoreTest( + "aaa", + 0.0, + 1, + listOf("a", "aa", "aaa"), + "notNull", + NestedObject("ddd"), + listOf(NestedObject("l1"), NestedObject("l2"), NestedObject("l3")), + 100.milliseconds, + ) + val testTwo = FirestoreTest( + "bbb", + 0.0, + 2, + listOf("b", "bb", "ccc"), + null, + NestedObject("eee"), + listOf(NestedObject("l2"), NestedObject("l4"), NestedObject("l5")), + 200.milliseconds, + ) + val testThree = FirestoreTest( + "ccc", + 1.0, + 3, + listOf("c", "cc", "ccc"), + "notNull", + NestedObject("fff"), + listOf(NestedObject("l3"), NestedObject("l6"), NestedObject("l7")), + 300.milliseconds, + ) + } + + private val collection get() = firestore.collection(COLLECTION) + + @Test + fun testStringOrderBy() = runTestWithFirestoreData { + val resultDocs = collection + .orderBy(FirestoreTest::prop1.name) + .get() + .documents + assertEquals(3, resultDocs.size) + assertEquals("aaa", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("ccc", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testFieldOrderBy() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FieldPath(FirestoreTest::prop1.name)).get().documents + assertEquals(3, resultDocs.size) + assertEquals("aaa", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("ccc", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testStringOrderByAscending() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING).get().documents + assertEquals(3, resultDocs.size) + assertEquals("aaa", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("ccc", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testFieldOrderByAscending() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FieldPath(FirestoreTest::prop1.name), Direction.ASCENDING).get().documents + assertEquals(3, resultDocs.size) + assertEquals("aaa", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("ccc", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testStringOrderByDescending() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FirestoreTest::prop1.name, Direction.DESCENDING).get().documents + assertEquals(3, resultDocs.size) + assertEquals("ccc", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("aaa", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testFieldOrderByDescending() = runTestWithFirestoreData { + val resultDocs = firestore.collection(COLLECTION) + .orderBy(FieldPath(FirestoreTest::prop1.name), Direction.DESCENDING).get().documents + assertEquals(3, resultDocs.size) + assertEquals("ccc", resultDocs[0].get(FirestoreTest::prop1.name)) + assertEquals("bbb", resultDocs[1].get(FirestoreTest::prop1.name)) + assertEquals("aaa", resultDocs[2].get(FirestoreTest::prop1.name)) + } + + @Test + fun testQueryEqualTo() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FirestoreTest::prop1.name equalTo testOne.prop1 } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::prop1.name) equalTo testTwo.prop1 } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo) + + val nullableQuery = collection + .where { FieldPath(FirestoreTest::optional.name).isNull } + + nullableQuery.assertDocuments(FirestoreTest.serializer(), testTwo) + + val serializeFieldQuery = collection.where { + FirestoreTest::nested.name.equalTo(NestedObject.serializer(), testOne.nested!!) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nested.name).equalTo(NestedObject.serializer(), testTwo.nested!!) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testTwo) + } + + @Test + fun testQueryNotEqualTo() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FirestoreTest::prop1.name notEqualTo testOne.prop1 } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::prop1.name) notEqualTo testTwo.prop1 } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) + + val nullableQuery = collection + .where { FieldPath(FirestoreTest::optional.name).isNotNull } + + nullableQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) + + val serializeFieldQuery = collection.where { + FirestoreTest::nested.name.notEqualTo(NestedObject.serializer(), testOne.nested!!) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nested.name).notEqualTo(NestedObject.serializer(), testTwo.nested!!) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) + } + + @Test + fun testQueryLessThan() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "count" lessThan testThree.count } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::count.name) lessThan testTwo.count } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val serializeFieldQuery = collection.where { + "duration".lessThan(DurationAsIntSerializer(), testThree.duration) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::duration.name).lessThan(DurationAsIntSerializer(), testTwo.duration) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testQueryGreaterThan() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "count" greaterThan testOne.count } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::count.name) greaterThan testTwo.count } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val serializeFieldQuery = collection.where { + "duration".greaterThan(DurationAsIntSerializer(), testOne.duration) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::duration.name).greaterThan(DurationAsIntSerializer(), testTwo.duration) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + } + + @Test + fun testQueryLessThanOrEqualTo() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "count" lessThanOrEqualTo testOne.count } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::count.name) lessThanOrEqualTo testTwo.count } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializeFieldQuery = collection.where { + "duration".lessThanOrEqualTo(DurationAsIntSerializer(), testOne.duration) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::duration.name).lessThanOrEqualTo(DurationAsIntSerializer(), testTwo.duration) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + } + + @Test + fun testQueryGreaterThanOrEqualTo() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "count" greaterThanOrEqualTo testThree.count } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::count.name) greaterThanOrEqualTo testTwo.count } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val serializeFieldQuery = collection.where { + "duration".greaterThanOrEqualTo(DurationAsIntSerializer(), testTwo.duration) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::duration.name).greaterThanOrEqualTo(DurationAsIntSerializer(), testThree.duration) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + } + + @Test + fun testQueryArrayContains() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "list" contains "a" } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::list.name) contains "ccc" } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testThree, testTwo) + + val serializeFieldQuery = collection.where { + "nestedList".contains(NestedObject.serializer(), NestedObject("l2")) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nestedList.name).contains(NestedObject.serializer(), NestedObject("l3")) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) + } + + @Test + fun testQueryArrayContainsAny() = runTestWithFirestoreData { + val fieldQuery = collection + .where { "list" containsAny listOf("a", "b") } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::list.name) containsAny listOf("c", "d") } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val serializeFieldQuery = collection.where { + "nestedList".containsAny(NestedObject.serializer(), listOf(NestedObject("l1"), NestedObject("l4"))) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nestedList.name).containsAny(NestedObject.serializer(), listOf(NestedObject("l5"), NestedObject("l7"))) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree, testTwo) + } + + @Test + fun testQueryInArray() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FirestoreTest::prop1.name inArray listOf("aaa", "bbb") } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::prop1.name) inArray listOf("ccc", "ddd") } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val serializeFieldQuery = collection.where { + FirestoreTest::nested.name.inArray(NestedObject.serializer(), listOf(NestedObject("ddd"), NestedObject("eee"))) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nested.name).inArray(NestedObject.serializer(), listOf(NestedObject("eee"), NestedObject("fff"))) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testThree, testTwo) + } + + @Test + fun testQueryNotInArray() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FirestoreTest::prop1.name notInArray listOf("aaa", "bbb") } + + fieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val pathQuery = collection + .where { FieldPath(FirestoreTest::prop1.name) notInArray listOf("ccc", "ddd") } + + pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val serializeFieldQuery = collection.where { + FirestoreTest::nested.name.notInArray(NestedObject.serializer(), listOf(NestedObject("ddd"), NestedObject("eee"))) + } + serializeFieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) + + val serializePathQuery = collection.where { + FieldPath(FirestoreTest::nested.name).notInArray(NestedObject.serializer(), listOf(NestedObject("eee"), NestedObject("fff"))) + } + serializePathQuery.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testCompoundQuery() = runTestWithFirestoreData { + val andQuery = collection + .where { + FieldPath(FirestoreTest::prop1.name) inArray listOf("aaa", "bbb") and (FieldPath(FirestoreTest::count.name) equalTo 1) + } + andQuery.assertDocuments(FirestoreTest.serializer(), testOne) + + val orQuery = collection + .where { + FieldPath(FirestoreTest::prop1.name) equalTo "aaa" or (FieldPath(FirestoreTest::count.name) equalTo 2) + } + orQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val andOrQuery = collection + .where { + all( + any( + FieldPath(FirestoreTest::prop1.name) equalTo "aaa", + FieldPath(FirestoreTest::count.name) equalTo 2, + )!!, + FieldPath(FirestoreTest::list.name) contains "a", + ) + } + andOrQuery.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testQueryByDocumentId() = runTestWithFirestoreData { + val fieldQuery = collection + .where { FieldPath.documentId equalTo "one" } + fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testQueryByTimestamp() = runTest { + @Serializable + data class DocumentWithTimestamp( + val time: Timestamp, + ) + + val collection = firestore + .collection("testQueryByTimestamp") + + val timestamp = Timestamp.fromMilliseconds(1693262549000.0) + + val pastTimestamp = Timestamp(timestamp.seconds - 60, 12345000) // note: iOS truncates 3 last digits of nanoseconds due to internal conversions + val futureTimestamp = Timestamp(timestamp.seconds + 60, 78910000) + + val doc1 = collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(pastTimestamp)) + val doc2 = collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(futureTimestamp)) + + try { + val equalityQueryResult = collection.where { + FieldPath(DocumentWithTimestamp::time.name) equalTo pastTimestamp + }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet() + + assertEquals(setOf(DocumentWithTimestamp(pastTimestamp)), equalityQueryResult) + + val gtQueryResult = collection.where { + FieldPath(DocumentWithTimestamp::time.name) greaterThan timestamp + }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet() + + assertEquals(setOf(DocumentWithTimestamp(futureTimestamp)), gtQueryResult) + } finally { + doc1.delete() + doc2.delete() + } + } + + @Test + fun testStartAfterDocumentSnapshot() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val firstPage = query.limit(2) + + firstPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + val lastDocumentSnapshot = firstPage.get().documents.lastOrNull() + assertNotNull(lastDocumentSnapshot) + + val secondPage = query.startAfter(lastDocumentSnapshot) + secondPage.assertDocuments(FirestoreTest.serializer(), testThree) + } + + @Test + fun testStartAfterFieldValues() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + query.assertDocuments(FirestoreTest.serializer(), testOne, testTwo, testThree) + + val secondPage = query.startAfter("bbb") + secondPage.assertDocuments(FirestoreTest.serializer(), testThree) + + val encodedQuery = collection + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedSecondPage = encodedQuery.startAfter { + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedSecondPage.assertDocuments(FirestoreTest.serializer(), testThree) + + val multipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val multipleSecondPage = multipleQuery.startAfter(0.0, "aaa") + multipleSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val encodedMultipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedMultipleSecondPage = encodedMultipleQuery.startAfter { + add(0.0) + addWithStrategy(NestedObject.serializer(), NestedObject("ddd")) + } + encodedMultipleSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + } + + @Test + fun testStartAtDocumentSnapshot() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val firstPage = query.limit(2) + firstPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val lastDocumentSnapshot = firstPage.get().documents.lastOrNull() + assertNotNull(lastDocumentSnapshot) + + val secondPage = query.startAt(lastDocumentSnapshot) + secondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + } + + @Test + fun testStartAtFieldValues() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val secondPage = query.startAt("bbb") + secondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val encodedQuery = collection + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedSecondPage = encodedQuery.startAt { + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val multipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val multipleSecondPage = multipleQuery.startAt(0.0, "bbb") + multipleSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + + val encodedMultipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedMultipleSecondPage = encodedMultipleQuery.startAt { + add(0.0) + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedMultipleSecondPage.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) + } + + @Test + fun testEndBeforeDocumentSnapshot() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val firstPage = query.limit(2) + firstPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val lastDocumentSnapshot = firstPage.get().documents.lastOrNull() + assertNotNull(lastDocumentSnapshot) + + val secondPage = query.endBefore(lastDocumentSnapshot) + secondPage.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testEndBeforeFieldValues() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val secondPage = query.endBefore("bbb") + secondPage.assertDocuments(FirestoreTest.serializer(), testOne) + + val encodedQuery = collection + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedSecondPage = encodedQuery.endBefore { + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + + val multipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val multipleSecondPage = multipleQuery.endBefore(0.0, "bbb") + multipleSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + + val encodedMultipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedMultipleSecondPage = encodedMultipleQuery.endBefore { + add(0.0) + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedMultipleSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + } + + @Test + fun testEndAtDocumentSnapshot() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val firstPage = query.limit(2) // First 2 results + firstPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val lastDocumentSnapshot = firstPage.get().documents.lastOrNull() + assertNotNull(lastDocumentSnapshot) + + val secondPage = query.endAt(lastDocumentSnapshot) + secondPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + } + + @Test + fun testEndAtFieldValues() = runTestWithFirestoreData { + val query = collection + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val secondPage = query.endAt("bbb") + secondPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val encodedQuery = collection + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedSecondPage = encodedQuery.endAt { + addWithStrategy(NestedObject.serializer(), NestedObject("eee")) + } + encodedSecondPage.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) + + val multipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::prop1.name, Direction.ASCENDING) + + val multipleSecondPage = multipleQuery.endAt(0.0, "aaa") + multipleSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + + val encodedMultipleQuery = collection + .orderBy(FieldPath(FirestoreTest::time.name), Direction.ASCENDING) + .orderBy(FirestoreTest::nested.name, Direction.ASCENDING) + + val encodedMultipleSecondPage = encodedMultipleQuery.endAt { + add(0.0) + addWithStrategy(NestedObject.serializer(), NestedObject("ddd")) + } + encodedMultipleSecondPage.assertDocuments(FirestoreTest.serializer(), testOne) + } + + private fun runTestWithFirestoreData( + documentOne: FirestoreTest = testOne, + documentTwo: FirestoreTest = testTwo, + documentThree: FirestoreTest = testThree, + block: suspend () -> Unit, + ) = runTest { + try { + setupFirestoreData(documentOne, documentTwo, documentThree) + block() + } finally { + cleanFirestoreData() + } + } + + private suspend fun setupFirestoreData( + documentOne: FirestoreTest = testOne, + documentTwo: FirestoreTest = testTwo, + documentThree: FirestoreTest = testThree, + ) { + firestore.collection(COLLECTION) + .document("one") + .set(FirestoreTest.serializer(), documentOne) + firestore.collection(COLLECTION) + .document("two") + .set(FirestoreTest.serializer(), documentTwo) + firestore.collection(COLLECTION) + .document("three") + .set(FirestoreTest.serializer(), documentThree) + } + + private suspend fun cleanFirestoreData() { + firestore.collection(COLLECTION).document("one").delete() + firestore.collection(COLLECTION).document("two").delete() + firestore.collection(COLLECTION).document("three").delete() + } + + private suspend fun Query.assertDocuments(serializer: KSerializer, vararg expected: T) { + val documents = get().documents + assertEquals(expected.size, documents.size) + documents.forEachIndexed { index, documentSnapshot -> + assertEquals(expected[index], documentSnapshot.data(serializer)) + } + } +} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt new file mode 100644 index 000000000..5a68d53a8 --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/TransactionTest.kt @@ -0,0 +1,53 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.milliseconds + +@IgnoreForAndroidUnitTest +class TransactionTest : BaseFirebaseFirestoreTest() { + + @Test + fun runTransaction() = runTest { + val collection = firestore.collection("testServerTestTransaction") + val document = collection.document("doc1") + try { + document.set( + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1", + count = 0, + ), + ) + val result = firestore.runTransaction { + val count = get(document).data(FirestoreTest.serializer()).count + + if (count < 1) { + update(document) { + FirestoreTest::prop1.name to "newProperty" + FieldPath(FirestoreTest::count.name) to 5 + FirestoreTest::duration.name.to(DurationAsIntSerializer(), 100.milliseconds) + FieldPath(FirestoreTest::nested.name).to( + NestedObject.serializer(), + NestedObject("nested"), + ) + } + true + } else { + throw IllegalStateException("Invalid count") + } + } + assertTrue(result) + + val updated = document.get().data(FirestoreTest.serializer()) + assertEquals("newProperty", updated.prop1) + assertEquals(5, updated.count) + assertEquals(100.milliseconds, updated.duration) + assertEquals(NestedObject("nested"), updated.nested) + } finally { + document.delete() + } + } +} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt new file mode 100644 index 000000000..f5e8ece3f --- /dev/null +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/WriteBatchTest.kt @@ -0,0 +1,210 @@ +package dev.gitlive.firebase.firestore + +import dev.gitlive.firebase.runTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.time.Duration.Companion.milliseconds + +@IgnoreForAndroidUnitTest +class WriteBatchTest : BaseFirebaseFirestoreTest() { + + private val collection get() = firestore + .collection("testServerTestSetBatch") + + @Test + fun testSetBatch() = testBatch { doc1, doc2 -> + val batch = firestore.batch() + batch.set( + documentRef = doc1, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1", + time = 123.0, + ), + ) + batch.set( + documentRef = doc2, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop2", + time = 456.0, + ), + ) + batch.commit() + + assertEquals("prop1", doc1.get().data(FirestoreTest.serializer()).prop1) + assertEquals("prop2", doc2.get().data(FirestoreTest.serializer()).prop1) + } + + @Test + fun testSetBatchDoesNotEncodeEmptyValues() = testBatch { doc1, doc2 -> + val batch = firestore.batch() + batch.set( + documentRef = doc1, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1-set", + time = 125.0, + ), + ) + batch.set( + documentRef = doc2, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop2-set", + time = 250.0, + ), + ) + batch.commit() + + assertEquals(125.0, doc1.get().get("time") as Double?) + assertEquals("prop1-set", doc1.get().data(FirestoreTest.serializer()).prop1) + assertEquals(250.0, doc2.get().get("time") as Double?) + assertEquals("prop2-set", doc2.get().data(FirestoreTest.serializer()).prop1) + } + + @Test + fun testUpdateBatch() = testBatch { doc1, doc2 -> + doc1.set( + FirestoreTest( + prop1 = "prop1", + time = 123.0, + ), + ) + doc2.set( + FirestoreTest( + prop1 = "prop2", + time = 456.0, + ), + ) + + val batch = firestore.batch() + batch.update( + documentRef = doc1, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1-updated", + time = 345.0, + ), + ) { + encodeDefaults = false + } + batch.update( + documentRef = doc2, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop2-updated", + time = 567.0, + ), + ) { + encodeDefaults = false + } + batch.commit() + + assertEquals("prop1-updated", doc1.get().data(FirestoreTest.serializer()).prop1) + assertEquals("prop2-updated", doc2.get().data(FirestoreTest.serializer()).prop1) + } + + @Test + fun testUpdateBatchDoesNotEncodeEmptyValues() = testBatch { doc1, doc2 -> + doc1.set( + FirestoreTest( + prop1 = "prop1", + time = 123.0, + ), + ) + doc2.set( + FirestoreTest( + prop1 = "prop2", + time = 456.0, + ), + ) + val batch = firestore.batch() + batch.update( + documentRef = doc1, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop1-set", + time = 126.0, + ), + ) { + encodeDefaults = false + } + batch.update( + documentRef = doc2, + strategy = FirestoreTest.serializer(), + data = FirestoreTest( + prop1 = "prop2-set", + time = 457.0, + ), + ) { + encodeDefaults = false + } + batch.commit() + + assertEquals(126.0, doc1.get().get("time") as Double?) + assertEquals("prop1-set", doc1.get().data(FirestoreTest.serializer()).prop1) + assertEquals(457.0, doc2.get().get("time") as Double?) + assertEquals("prop2-set", doc2.get().data(FirestoreTest.serializer()).prop1) + } + + @Test + fun testUpdateFieldValuesBatch() = testBatch { doc1, doc2 -> + doc1.set( + FirestoreTest( + prop1 = "prop1", + time = 123.0, + duration = 800.milliseconds, + ), + ) + + doc2.set( + FirestoreTest( + prop1 = "prop2", + time = 456.0, + duration = 700.milliseconds, + ), + ) + + val batch = firestore.batch() + batch.update(doc1) { + FirestoreTest::prop1.name to "prop1-updated" + FieldPath(FirestoreTest::optional.name) to "notNull" + FirestoreTest::duration.name.to(DurationAsIntSerializer(), 300.milliseconds) + FieldPath(FirestoreTest::nested.name).to(NestedObject.serializer(), NestedObject("nested")) + } + batch.update(doc2) { + FirestoreTest::prop1.name to "prop2-updated" + FieldPath(FirestoreTest::optional.name) to "alsoNotNull" + FirestoreTest::duration.name.to(DurationAsIntSerializer(), 200.milliseconds) + FieldPath(FirestoreTest::nested.name).to(NestedObject.serializer(), NestedObject("alsoNested")) + } + batch.commit() + + val updatedDoc1 = doc1.get().data(FirestoreTest.serializer()) + assertEquals("prop1-updated", updatedDoc1.prop1) + assertEquals("notNull", updatedDoc1.optional) + assertEquals(300.milliseconds, updatedDoc1.duration) + assertEquals(NestedObject("nested"), updatedDoc1.nested) + + val updatedDoc2 = doc2.get().data(FirestoreTest.serializer()) + assertEquals("prop2-updated", updatedDoc2.prop1) + assertEquals("alsoNotNull", updatedDoc2.optional) + assertEquals(200.milliseconds, updatedDoc2.duration) + assertEquals(NestedObject("alsoNested"), updatedDoc2.nested) + } + + private fun testBatch(block: suspend (DocumentReference, DocumentReference) -> Unit) = runTest { + val doc1 = collection + .document("test1") + val doc2 = collection + .document("test2") + + try { + block(doc1, doc2) + } finally { + doc1.delete() + doc2.delete() + } + } +} diff --git a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt index 50b90c44c..7f79a7ebc 100644 --- a/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt +++ b/firebase-firestore/src/commonTest/kotlin/dev/gitlive/firebase/firestore/firestore.kt @@ -8,31 +8,19 @@ import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.FirebaseApp import dev.gitlive.firebase.FirebaseOptions import dev.gitlive.firebase.apps -import dev.gitlive.firebase.internal.decode import dev.gitlive.firebase.initialize import dev.gitlive.firebase.runBlockingTest import dev.gitlive.firebase.runTest -import dev.gitlive.firebase.withSerializer -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.withContext import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.builtins.ListSerializer -import kotlinx.serialization.builtins.nullable -import kotlinx.serialization.builtins.serializer -import kotlin.random.Random +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals -import kotlin.test.assertNotNull -import kotlin.test.assertNull -import kotlin.test.assertTrue import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -46,7 +34,7 @@ expect fun encodedAsMap(encoded: Any?): Map expect fun Map.asEncoded(): Any @IgnoreForAndroidUnitTest -class FirebaseFirestoreTest { +abstract class BaseFirebaseFirestoreTest { @Serializable data class FirestoreTest( @@ -55,35 +43,27 @@ class FirebaseFirestoreTest { val count: Int = 0, val list: List = emptyList(), val optional: String? = null, + val nested: NestedObject? = null, + val nestedList: List = emptyList(), + @Serializable(with = DurationAsIntSerializer::class) + val duration: Duration = Duration.ZERO, ) @Serializable - data class FirestoreTimeTest( - val prop1: String, - val time: BaseTimestamp?, + data class NestedObject( + val prop2: String, ) - companion object { - val testOne = FirestoreTest( - "aaa", - 0.0, - 1, - listOf("a", "aa", "aaa"), - "notNull", - ) - val testTwo = FirestoreTest( - "bbb", - 0.0, - 2, - listOf("b", "bb", "ccc"), - ) - val testThree = FirestoreTest( - "ccc", - 1.0, - 3, - listOf("c", "cc", "ccc"), - "notNull", - ) + // Long would be better but JS does not seem to support it on the Firebase level https://stackoverflow.com/questions/31930406/storing-long-type-in-firebase + class DurationAsIntSerializer : KSerializer { + + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("millisecondsSinceEpoch", PrimitiveKind.INT) + + override fun serialize(encoder: Encoder, value: Duration) { + encoder.encodeInt(value.inWholeMilliseconds.toInt()) + } + + override fun deserialize(decoder: Decoder): Duration = decoder.decodeInt().milliseconds } lateinit var firebaseApp: FirebaseApp @@ -120,951 +100,14 @@ class FirebaseFirestoreTest { it.delete() } } +} - @Test - fun testStringOrderBy() = runTest { - setupFirestoreData() - val resultDocs = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1") - .get() - .documents - assertEquals(3, resultDocs.size) - assertEquals("aaa", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("ccc", resultDocs[2].get("prop1")) - } - - @Test - fun testFieldOrderBy() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy(FieldPath("prop1")).get().documents - assertEquals(3, resultDocs.size) - assertEquals("aaa", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("ccc", resultDocs[2].get("prop1")) - } - - @Test - fun testStringOrderByAscending() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING).get().documents - assertEquals(3, resultDocs.size) - assertEquals("aaa", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("ccc", resultDocs[2].get("prop1")) - } - - @Test - fun testFieldOrderByAscending() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy(FieldPath("prop1"), Direction.ASCENDING).get().documents - assertEquals(3, resultDocs.size) - assertEquals("aaa", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("ccc", resultDocs[2].get("prop1")) - } - - @Test - fun testStringOrderByDescending() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy("prop1", Direction.DESCENDING).get().documents - assertEquals(3, resultDocs.size) - assertEquals("ccc", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("aaa", resultDocs[2].get("prop1")) - } - - @Test - fun testFieldOrderByDescending() = runTest { - setupFirestoreData() - - val resultDocs = firestore.collection("testFirestoreQuerying") - .orderBy(FieldPath("prop1"), Direction.DESCENDING).get().documents - assertEquals(3, resultDocs.size) - assertEquals("ccc", resultDocs[0].get("prop1")) - assertEquals("bbb", resultDocs[1].get("prop1")) - assertEquals("aaa", resultDocs[2].get("prop1")) - } - - @Test - fun testServerTimestampFieldValue() = runTest { - val doc = firestore - .collection("testServerTimestampFieldValue") - .document("test") - doc.set( - FirestoreTimeTest.serializer(), - FirestoreTimeTest("ServerTimestamp", Timestamp(123, 0)), - ) - assertEquals(Timestamp(123, 0), doc.get().get("time", TimestampSerializer)) - - doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestamp", Timestamp.ServerTimestamp)) - - assertNotEquals(Timestamp.ServerTimestamp, doc.get().get("time", BaseTimestamp.serializer())) - assertNotEquals(Timestamp.ServerTimestamp, doc.get().data(FirestoreTimeTest.serializer()).time) - } - - @Test - fun testServerTimestampBehaviorNone() = runTest { - val doc = firestore - .collection("testServerTimestampBehaviorNone") - .document("test${Random.nextInt()}") - - val deferredPendingWritesSnapshot = async { - doc.snapshots.filter { it.exists }.first() - } - nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot - - doc.set( - FirestoreTimeTest.serializer(), - FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp), - ) - - val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() - assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) - assertNull(pendingWritesSnapshot.get("time", BaseTimestamp.serializer().nullable, serverTimestampBehavior = ServerTimestampBehavior.NONE)) - } - - @Test - fun testSetBatch() = runTest { - val doc = firestore - .collection("testServerTestSetBatch") - .document("test") - val batch = firestore.batch() - batch.set( - documentRef = doc, - strategy = FirestoreTest.serializer(), - data = FirestoreTest( - prop1 = "prop1", - time = 123.0, - ), - ) - batch.commit() - - assertEquals("prop1", doc.get().data(FirestoreTest.serializer()).prop1) - } - - @Test - fun testServerTimestampBehaviorEstimate() = runTest { - val doc = firestore - .collection("testServerTimestampBehaviorEstimate") - .document("test${Random.nextInt()}") - - val deferredPendingWritesSnapshot = async { - doc.snapshots.filter { it.exists }.first() - } - nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot - - doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp)) - - val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() - assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) - assertNotNull(pendingWritesSnapshot.get("time", ServerTimestampBehavior.ESTIMATE)) - assertNotEquals(Timestamp.ServerTimestamp, pendingWritesSnapshot.data(FirestoreTimeTest.serializer(), serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE).time) - } - - @Test - fun testServerTimestampBehaviorPrevious() = runTest { - val doc = firestore - .collection("testServerTimestampBehaviorPrevious") - .document("test${Random.nextInt()}") - - val deferredPendingWritesSnapshot = async { - doc.snapshots.filter { it.exists }.first() - } - nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot - - doc.set(FirestoreTimeTest.serializer(), FirestoreTimeTest("ServerTimestampBehavior", Timestamp.ServerTimestamp)) - - val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() - assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) - assertNull(pendingWritesSnapshot.get("time", BaseTimestamp.serializer().nullable, serverTimestampBehavior = ServerTimestampBehavior.PREVIOUS)) - } - - @Test - fun testDocumentAutoId() = runTest { - val doc = firestore - .collection("testDocumentAutoId") - .document - - doc.set(FirestoreTest.serializer(), FirestoreTest("AutoId")) - - val resultDoc = firestore - .collection("testDocumentAutoId") - .document(doc.id) - .get() - - assertEquals(true, resultDoc.exists) - assertEquals("AutoId", resultDoc.get("prop1")) - } - - @Test - fun testStartAfterDocumentSnapshot() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.limit(2).get().documents // First 2 results - assertEquals(2, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - - val lastDocumentSnapshot = firstPage.lastOrNull() - assertNotNull(lastDocumentSnapshot) - - val secondPage = query.startAfter(lastDocumentSnapshot).get().documents - assertEquals(1, secondPage.size) - assertEquals("ccc", secondPage[0].get("prop1")) - } - - @Test - fun testStartAfterFieldValues() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.get().documents - assertEquals(3, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - assertEquals("ccc", firstPage[2].get("prop1")) - - val secondPage = query.startAfter("bbb").get().documents - assertEquals(1, secondPage.size) - assertEquals("ccc", secondPage[0].get("prop1")) - } - - @Test - fun testStartAtDocumentSnapshot() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.limit(2).get().documents // First 2 results - assertEquals(2, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - - val lastDocumentSnapshot = firstPage.lastOrNull() - assertNotNull(lastDocumentSnapshot) - - val secondPage = query.startAt(lastDocumentSnapshot).get().documents - assertEquals(2, secondPage.size) - assertEquals("bbb", secondPage[0].get("prop1")) - assertEquals("ccc", secondPage[1].get("prop1")) - } - - @Test - fun testStartAtFieldValues() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.get().documents // First 2 results - assertEquals(3, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - assertEquals("ccc", firstPage[2].get("prop1")) - - val secondPage = query.startAt("bbb").get().documents - assertEquals(2, secondPage.size) - assertEquals("bbb", secondPage[0].get("prop1")) - assertEquals("ccc", secondPage[1].get("prop1")) - } - - @Test - fun testEndBeforeDocumentSnapshot() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.limit(2).get().documents // First 2 results - assertEquals(2, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - - val lastDocumentSnapshot = firstPage.lastOrNull() - assertNotNull(lastDocumentSnapshot) - - val secondPage = query.endBefore(lastDocumentSnapshot).get().documents - assertEquals(1, secondPage.size) - assertEquals("aaa", secondPage[0].get("prop1")) - } - - @Test - fun testEndBeforeFieldValues() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.get().documents - assertEquals(3, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - assertEquals("ccc", firstPage[2].get("prop1")) - - val secondPage = query.endBefore("bbb").get().documents - assertEquals(1, secondPage.size) - assertEquals("aaa", secondPage[0].get("prop1")) - } - - @Test - fun testEndAtDocumentSnapshot() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.limit(2).get().documents // First 2 results - assertEquals(2, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - - val lastDocumentSnapshot = firstPage.lastOrNull() - assertNotNull(lastDocumentSnapshot) - - val secondPage = query.endAt(lastDocumentSnapshot).get().documents - assertEquals(2, secondPage.size) - assertEquals("aaa", secondPage[0].get("prop1")) - assertEquals("bbb", secondPage[1].get("prop1")) - } - - @Test - fun testEndAtFieldValues() = runTest { - setupFirestoreData() - val query = firestore - .collection("testFirestoreQuerying") - .orderBy("prop1", Direction.ASCENDING) - - val firstPage = query.get().documents // First 2 results - assertEquals(3, firstPage.size) - assertEquals("aaa", firstPage[0].get("prop1")) - assertEquals("bbb", firstPage[1].get("prop1")) - assertEquals("ccc", firstPage[2].get("prop1")) - - val secondPage = query.endAt("bbb").get().documents - assertEquals(2, secondPage.size) - assertEquals("aaa", secondPage[0].get("prop1")) - assertEquals("bbb", secondPage[1].get("prop1")) - } - - @Test - fun testIncrementFieldValue() = runTest { - val doc = firestore - .collection("testFirestoreIncrementFieldValue") - .document("test1") - - doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", count = 0)) - val dataBefore = doc.get().data(FirestoreTest.serializer()) - assertEquals(0, dataBefore.count) - - doc.update("count" to FieldValue.increment(5)) - val dataAfter = doc.get().data(FirestoreTest.serializer()) - assertEquals(5, dataAfter.count) - } - - @Test - fun testArrayUnion() = runTest { - val doc = firestore - .collection("testFirestoreArrayUnion") - .document("test1") - - doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first"))) - val dataBefore = doc.get().data(FirestoreTest.serializer()) - assertEquals(listOf("first"), dataBefore.list) - - doc.update("list" to FieldValue.arrayUnion("second")) - val dataAfter = doc.get().data(FirestoreTest.serializer()) - assertEquals(listOf("first", "second"), dataAfter.list) - } - - @Test - fun testArrayRemove() = runTest { - val doc = firestore - .collection("testFirestoreArrayRemove") - .document("test1") - - doc.set(FirestoreTest.serializer(), FirestoreTest("increment1", list = listOf("first", "second"))) - val dataBefore = doc.get().data(FirestoreTest.serializer()) - assertEquals(listOf("first", "second"), dataBefore.list) - - doc.update("list" to FieldValue.arrayRemove("second")) - val dataAfter = doc.get().data(FirestoreTest.serializer()) - assertEquals(listOf("first"), dataAfter.list) - } - - @Test - fun testSetBatchDoesNotEncodeEmptyValues() = runTest { - val doc = firestore - .collection("testServerTestSetBatch") - .document("test") - val batch = firestore.batch() - batch.set( - documentRef = doc, - strategy = FirestoreTest.serializer(), - data = FirestoreTest( - prop1 = "prop1-set", - time = 125.0, - ), - ) - batch.commit() - - assertEquals(125.0, doc.get().get("time") as Double?) - assertEquals("prop1-set", doc.get().data(FirestoreTest.serializer()).prop1) - } - - @Test - fun testUpdateBatch() = runTest { - val doc = firestore - .collection("testServerTestSetBatch") - .document("test").apply { - set( - FirestoreTest( - prop1 = "prop1", - time = 123.0, - ), - ) - } - - val batch = firestore.batch() - batch.update( - documentRef = doc, - strategy = FirestoreTest.serializer(), - data = FirestoreTest( - prop1 = "prop1-updated", - time = 123.0, - ), - ) { - encodeDefaults = false - } - batch.commit() - - assertEquals("prop1-updated", doc.get().data(FirestoreTest.serializer()).prop1) - } - - @Test - fun testUpdateBatchDoesNotEncodeEmptyValues() = runTest { - val doc = firestore - .collection("testServerTestSetBatch") - .document("test").apply { - set( - FirestoreTest( - prop1 = "prop1", - time = 123.0, - ), - ) - } - val batch = firestore.batch() - batch.update( - documentRef = doc, - strategy = FirestoreTest.serializer(), - data = FirestoreTest( - prop1 = "prop1-set", - time = 126.0, - ), - ) { - encodeDefaults = false - } - batch.commit() - - assertEquals(126.0, doc.get().get("time") as Double?) - assertEquals("prop1-set", doc.get().data(FirestoreTest.serializer()).prop1) - } - - @Test - fun testLegacyDoubleTimestamp() = runTest { - @Serializable - data class DoubleTimestamp( - @Serializable(with = DoubleAsTimestampSerializer::class) - val time: Double?, - ) - - val doc = firestore - .collection("testLegacyDoubleTimestamp") - .document("test${Random.nextInt()}") - - val deferredPendingWritesSnapshot = async { - doc.snapshots.filter { it.exists }.first() - } - nonSkippedDelay(100.milliseconds) // makes possible to catch pending writes snapshot - - doc.set(DoubleTimestamp.serializer(), DoubleTimestamp(DoubleAsTimestampSerializer.SERVER_TIMESTAMP)) - - val pendingWritesSnapshot = deferredPendingWritesSnapshot.await() - assertTrue(pendingWritesSnapshot.metadata.hasPendingWrites) - assertNotNull(pendingWritesSnapshot.get("time", DoubleAsTimestampSerializer, serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE)) - assertNotEquals(DoubleAsTimestampSerializer.SERVER_TIMESTAMP, pendingWritesSnapshot.data(DoubleTimestamp.serializer(), serverTimestampBehavior = ServerTimestampBehavior.ESTIMATE).time) - } - - @Test - fun testLegacyDoubleTimestampWriteNewFormatRead() = runTest { - @Serializable - data class LegacyDocument( - @Serializable(with = DoubleAsTimestampSerializer::class) - val time: Double, - ) - - @Serializable - data class NewDocument( - val time: Timestamp, - ) - - val doc = firestore - .collection("testLegacyDoubleTimestampEncodeDecode") - .document("testLegacy") - - val ms = 12345678.0 - - doc.set(LegacyDocument.serializer(), LegacyDocument(time = ms)) - - val fetched: NewDocument = doc.get().data(NewDocument.serializer()) - assertEquals(ms, fetched.time.toMilliseconds()) - } - - @Test - fun testQueryByTimestamp() = runTest { - @Serializable - data class DocumentWithTimestamp( - val time: Timestamp, - ) - - val collection = firestore - .collection("testQueryByTimestamp") - - val timestamp = Timestamp.fromMilliseconds(1693262549000.0) - - val pastTimestamp = Timestamp(timestamp.seconds - 60, 12345000) // note: iOS truncates 3 last digits of nanoseconds due to internal conversions - val futureTimestamp = Timestamp(timestamp.seconds + 60, 78910000) - - collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(pastTimestamp)) - collection.add(DocumentWithTimestamp.serializer(), DocumentWithTimestamp(futureTimestamp)) - - val equalityQueryResult = collection.where { - FieldPath(DocumentWithTimestamp::time.name) equalTo pastTimestamp - }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet() - - assertEquals(setOf(DocumentWithTimestamp(pastTimestamp)), equalityQueryResult) - - val gtQueryResult = collection.where { - FieldPath(DocumentWithTimestamp::time.name) greaterThan timestamp - }.get().documents.map { it.data(DocumentWithTimestamp.serializer()) }.toSet() - - assertEquals(setOf(DocumentWithTimestamp(futureTimestamp)), gtQueryResult) - } - - @Test - fun testGeoPointSerialization() = runTest { - @Serializable - data class DataWithGeoPoint(val geoPoint: GeoPoint) - - fun getDocument() = firestore.collection("geoPointSerialization") - .document("geoPointSerialization") - - val data = DataWithGeoPoint(GeoPoint(12.34, 56.78)) - // store geo point - getDocument().set(DataWithGeoPoint.serializer(), data) - // restore data - val savedData = getDocument().get().data(DataWithGeoPoint.serializer()) - assertEquals(data.geoPoint, savedData.geoPoint) - - // update data - val updatedData = DataWithGeoPoint(GeoPoint(87.65, 43.21)) - getDocument().update(FieldPath(DataWithGeoPoint::geoPoint.name) to updatedData.geoPoint) - // verify update - val updatedSavedData = getDocument().get().data(DataWithGeoPoint.serializer()) - assertEquals(updatedData.geoPoint, updatedSavedData.geoPoint) - } - - @Test - fun testDocumentReferenceSerialization() = runTest { - @Serializable - data class DataWithDocumentReference( - val documentReference: DocumentReference, - ) - - fun getCollection() = firestore.collection("documentReferenceSerialization") - fun getDocument() = getCollection() - .document("documentReferenceSerialization") - val documentRef1 = getCollection().document("refDoc1").apply { - set(mapOf("value" to 1)) - } - val documentRef2 = getCollection().document("refDoc2").apply { - set(mapOf("value" to 2)) - } - - val data = DataWithDocumentReference(documentRef1) - // store reference - getDocument().set(DataWithDocumentReference.serializer(), data) - // restore data - val savedData = getDocument().get().data(DataWithDocumentReference.serializer()) - assertEquals(data.documentReference.path, savedData.documentReference.path) - - // update data - val updatedData = DataWithDocumentReference(documentRef2) - getDocument().update( - FieldPath(DataWithDocumentReference::documentReference.name) to updatedData.documentReference.withSerializer(DocumentReferenceSerializer), - ) - // verify update - val updatedSavedData = getDocument().get().data(DataWithDocumentReference.serializer()) - assertEquals(updatedData.documentReference.path, updatedSavedData.documentReference.path) - } - - @Serializable - data class TestDataWithDocumentReference( - val uid: String, - val reference: DocumentReference, - val optionalReference: DocumentReference?, - ) - - @Serializable - data class TestDataWithOptionalDocumentReference( - val optionalReference: DocumentReference?, - ) - - @Test - fun encodeDocumentReference() = runTest { - val doc = firestore.document("a/b") - val item = TestDataWithDocumentReference("123", doc, doc) - val encoded = encodedAsMap( - encode(item) { - encodeDefaults = false - }, - ) - assertEquals("123", encoded["uid"]) - assertEquals(doc.nativeValue, encoded["reference"]) - assertEquals(doc.nativeValue, encoded["optionalReference"]) - } - - @Test - fun encodeNullDocumentReference() = runTest { - val item = TestDataWithOptionalDocumentReference(null) - val encoded = encodedAsMap( - encode(item) { - encodeDefaults = false - }, - ) - assertNull(encoded["optionalReference"]) - } - - @Test - fun decodeDocumentReference() = runTest { - val doc = firestore.document("a/b") - val obj = mapOf( - "uid" to "123", - "reference" to doc.nativeValue, - "optionalReference" to doc.nativeValue, - ).asEncoded() - val decoded: TestDataWithDocumentReference = decode(obj) - assertEquals("123", decoded.uid) - assertEquals(doc.path, decoded.reference.path) - assertEquals(doc.path, decoded.optionalReference?.path) - } - - @Test - fun decodeNullDocumentReference() = runTest { - val obj = mapOf("optionalReference" to null).asEncoded() - val decoded: TestDataWithOptionalDocumentReference = decode(obj) - assertNull(decoded.optionalReference?.path) - } - - @Test - fun testFieldValuesOps() = runTest { - @Serializable - data class TestData(val values: List) - fun getDocument() = firestore.collection("fieldValuesOps") - .document("fieldValuesOps") - - val data = TestData(listOf(1)) - // store - getDocument().set(TestData.serializer(), data) - // append & verify - getDocument().update(FieldPath(TestData::values.name) to FieldValue.arrayUnion(2)) - - var savedData = getDocument().get().data(TestData.serializer()) - assertEquals(listOf(1, 2), savedData.values) - - // remove & verify - getDocument().update(FieldPath(TestData::values.name) to FieldValue.arrayRemove(1)) - savedData = getDocument().get().data(TestData.serializer()) - assertEquals(listOf(2), savedData.values) - - val list = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) - assertEquals(listOf(2), list) - // delete & verify - getDocument().update(FieldPath(TestData::values.name) to FieldValue.delete) - val deletedList = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) - assertNull(deletedList) - } - - @Test - fun testQueryEqualTo() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "prop1" equalTo testOne.prop1 } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::prop1.name) equalTo testTwo.prop1 } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo) - - val nullableQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::optional.name) equalTo null } - - nullableQuery.assertDocuments(FirestoreTest.serializer(), testTwo) - } - - @Test - fun testQueryNotEqualTo() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "prop1" notEqualTo testOne.prop1 } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::prop1.name) notEqualTo testTwo.prop1 } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) - - val nullableQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::optional.name) notEqualTo null } - - nullableQuery.assertDocuments(FirestoreTest.serializer(), testOne, testThree) - } - - @Test - fun testQueryLessThan() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "count" lessThan testThree.count } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::count.name) lessThan testTwo.count } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testOne) - } - - @Test - fun testQueryGreaterThan() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "count" greaterThan testOne.count } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::count.name) greaterThan testTwo.count } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) - } - - @Test - fun testQueryLessThanOrEqualTo() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "count" lessThanOrEqualTo testOne.count } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::count.name) lessThanOrEqualTo testTwo.count } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - } - - @Test - fun testQueryGreaterThanOrEqualTo() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "count" greaterThanOrEqualTo testThree.count } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::count.name) greaterThanOrEqualTo testTwo.count } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testTwo, testThree) - } - - @Test - fun testQueryArrayContains() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "list" contains "a" } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::list.name) contains "ccc" } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testThree, testTwo) - } - - @Test - fun testQueryArrayContainsAny() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "list" containsAny listOf("a", "b") } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::list.name) containsAny listOf("c", "d") } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) - } - - @Test - fun testQueryInArray() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "prop1" inArray listOf("aaa", "bbb") } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::prop1.name) inArray listOf("ccc", "ddd") } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testThree) - } - - @Test - fun testQueryNotInArray() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { "prop1" notInArray listOf("aaa", "bbb") } - - fieldQuery.assertDocuments(FirestoreTest.serializer(), testThree) - - val pathQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath(FirestoreTest::prop1.name) notInArray listOf("ccc", "ddd") } - - pathQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - } - - @Test - fun testCompoundQuery() = runTest { - setupFirestoreData() - - val andQuery = firestore - .collection("testFirestoreQuerying") - .where { - FieldPath(FirestoreTest::prop1.name) inArray listOf("aaa", "bbb") and (FieldPath(FirestoreTest::count.name) equalTo 1) - } - andQuery.assertDocuments(FirestoreTest.serializer(), testOne) - - val orQuery = firestore - .collection("testFirestoreQuerying") - .where { - FieldPath(FirestoreTest::prop1.name) equalTo "aaa" or (FieldPath(FirestoreTest::count.name) equalTo 2) - } - orQuery.assertDocuments(FirestoreTest.serializer(), testOne, testTwo) - - val andOrQuery = firestore - .collection("testFirestoreQuerying") - .where { - all( - any( - FieldPath(FirestoreTest::prop1.name) equalTo "aaa", - FieldPath(FirestoreTest::count.name) equalTo 2, - )!!, - FieldPath(FirestoreTest::list.name) contains "a", - ) - } - andOrQuery.assertDocuments(FirestoreTest.serializer(), testOne) - } - - @Test - fun testQueryByDocumentId() = runTest { - setupFirestoreData() - - val fieldQuery = firestore - .collection("testFirestoreQuerying") - .where { FieldPath.documentId equalTo "one" } - fieldQuery.assertDocuments(FirestoreTest.serializer(), testOne) - } +@IgnoreForAndroidUnitTest +class FirebaseFirestoreTest : BaseFirebaseFirestoreTest() { @Test fun testMultiple() = runTest { Firebase.firestore(firebaseApp).disableNetwork() Firebase.firestore(firebaseApp).enableNetwork() } - - private suspend fun setupFirestoreData( - documentOne: FirestoreTest = testOne, - documentTwo: FirestoreTest = testTwo, - documentThree: FirestoreTest = testThree, - ) { - firestore.collection("testFirestoreQuerying") - .document("one") - .set(FirestoreTest.serializer(), documentOne) - firestore.collection("testFirestoreQuerying") - .document("two") - .set(FirestoreTest.serializer(), documentTwo) - firestore.collection("testFirestoreQuerying") - .document("three") - .set(FirestoreTest.serializer(), documentThree) - } - - private suspend fun Query.assertDocuments(serializer: KSerializer, vararg expected: T) { - val documents = get().documents - assertEquals(expected.size, documents.size) - documents.forEachIndexed { index, documentSnapshot -> - assertEquals(expected[index], documentSnapshot.data(serializer)) - } - } - - private suspend fun nonSkippedDelay(timeout: Duration) = withContext(Dispatchers.Default) { - delay(timeout) - } } diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt index d0eb95602..8497833e8 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/Timestamp.kt @@ -1,6 +1,6 @@ package dev.gitlive.firebase.firestore -import cocoapods.FirebaseFirestoreInternal.FIRTimestamp +import cocoapods.FirebaseCore.FIRTimestamp import kotlinx.serialization.Serializable /** A class representing a platform specific Firebase Timestamp. */ diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index 98856556b..4f2c72d14 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -1,10 +1,10 @@ package dev.gitlive.firebase.firestore.internal -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.Source import dev.gitlive.firebase.firestore.await import dev.gitlive.firebase.firestore.awaitResult +import dev.gitlive.firebase.firestore.toEncodedMap import dev.gitlive.firebase.firestore.toException import dev.gitlive.firebase.internal.EncodedObject import dev.gitlive.firebase.internal.ios @@ -55,14 +55,9 @@ internal actual class NativeDocumentReference actual constructor(actual val nati ios.updateData(encodedData.ios, it) } - actual suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) = + actual suspend fun updateEncoded(encodedFieldsAndValues: List) = await { - ios.updateData(encodedFieldsAndValues.toMap(), it) - } - - actual suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) = - await { - ios.updateData(encodedFieldsAndValues.toMap(), it) + ios.updateData(encodedFieldsAndValues.toEncodedMap(), it) } actual suspend fun delete() = await { ios.deleteDocumentWithCompletion(it) } diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt index 30f01b7f1..f17bf14c8 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt @@ -45,28 +45,28 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is Filter.And -> FIRFilter.andFilterWithFilters(filters.map { it.toFIRFilter() }) is Filter.Or -> FIRFilter.orFilterWithFilters(filters.map { it.toFIRFilter() }) is Filter.Field -> when (constraint) { - is WhereConstraint.EqualTo -> FIRFilter.filterWhereField(field, isEqualTo = constraint.safeValue ?: NSNull.`null`()) - is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereField(field, isNotEqualTo = constraint.safeValue ?: NSNull.`null`()) - is WhereConstraint.LessThan -> FIRFilter.filterWhereField(field, isLessThan = constraint.safeValue) - is WhereConstraint.GreaterThan -> FIRFilter.filterWhereField(field, isGreaterThan = constraint.safeValue) - is WhereConstraint.LessThanOrEqualTo -> FIRFilter.filterWhereField(field, isLessThanOrEqualTo = constraint.safeValue) - is WhereConstraint.GreaterThanOrEqualTo -> FIRFilter.filterWhereField(field, isGreaterThanOrEqualTo = constraint.safeValue) - is WhereConstraint.ArrayContains -> FIRFilter.filterWhereField(field, arrayContains = constraint.safeValue) - is WhereConstraint.ArrayContainsAny -> FIRFilter.filterWhereField(field, arrayContainsAny = constraint.safeValues) - is WhereConstraint.InArray -> FIRFilter.filterWhereField(field, `in` = constraint.safeValues) - is WhereConstraint.NotInArray -> FIRFilter.filterWhereField(field, notIn = constraint.safeValues) + is WhereConstraint.EqualTo -> FIRFilter.filterWhereField(field, isEqualTo = constraint.value ?: NSNull.`null`()) + is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereField(field, isNotEqualTo = constraint.value ?: NSNull.`null`()) + is WhereConstraint.LessThan -> FIRFilter.filterWhereField(field, isLessThan = constraint.value) + is WhereConstraint.GreaterThan -> FIRFilter.filterWhereField(field, isGreaterThan = constraint.value) + is WhereConstraint.LessThanOrEqualTo -> FIRFilter.filterWhereField(field, isLessThanOrEqualTo = constraint.value) + is WhereConstraint.GreaterThanOrEqualTo -> FIRFilter.filterWhereField(field, isGreaterThanOrEqualTo = constraint.value) + is WhereConstraint.ArrayContains -> FIRFilter.filterWhereField(field, arrayContains = constraint.value) + is WhereConstraint.ArrayContainsAny -> FIRFilter.filterWhereField(field, arrayContainsAny = constraint.values) + is WhereConstraint.InArray -> FIRFilter.filterWhereField(field, `in` = constraint.values) + is WhereConstraint.NotInArray -> FIRFilter.filterWhereField(field, notIn = constraint.values) } is Filter.Path -> when (constraint) { - is WhereConstraint.EqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isEqualTo = constraint.safeValue ?: NSNull.`null`()) - is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isNotEqualTo = constraint.safeValue ?: NSNull.`null`()) - is WhereConstraint.LessThan -> FIRFilter.filterWhereFieldPath(path.ios, isLessThan = constraint.safeValue) - is WhereConstraint.GreaterThan -> FIRFilter.filterWhereFieldPath(path.ios, isGreaterThan = constraint.safeValue) - is WhereConstraint.LessThanOrEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isLessThanOrEqualTo = constraint.safeValue) - is WhereConstraint.GreaterThanOrEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isGreaterThanOrEqualTo = constraint.safeValue) - is WhereConstraint.ArrayContains -> FIRFilter.filterWhereFieldPath(path.ios, arrayContains = constraint.safeValue) - is WhereConstraint.ArrayContainsAny -> FIRFilter.filterWhereFieldPath(path.ios, arrayContainsAny = constraint.safeValues) - is WhereConstraint.InArray -> FIRFilter.filterWhereFieldPath(path.ios, `in` = constraint.safeValues) - is WhereConstraint.NotInArray -> FIRFilter.filterWhereFieldPath(path.ios, notIn = constraint.safeValues) + is WhereConstraint.EqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isEqualTo = constraint.value ?: NSNull.`null`()) + is WhereConstraint.NotEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isNotEqualTo = constraint.value ?: NSNull.`null`()) + is WhereConstraint.LessThan -> FIRFilter.filterWhereFieldPath(path.ios, isLessThan = constraint.value) + is WhereConstraint.GreaterThan -> FIRFilter.filterWhereFieldPath(path.ios, isGreaterThan = constraint.value) + is WhereConstraint.LessThanOrEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isLessThanOrEqualTo = constraint.value) + is WhereConstraint.GreaterThanOrEqualTo -> FIRFilter.filterWhereFieldPath(path.ios, isGreaterThanOrEqualTo = constraint.value) + is WhereConstraint.ArrayContains -> FIRFilter.filterWhereFieldPath(path.ios, arrayContains = constraint.value) + is WhereConstraint.ArrayContainsAny -> FIRFilter.filterWhereFieldPath(path.ios, arrayContainsAny = constraint.values) + is WhereConstraint.InArray -> FIRFilter.filterWhereFieldPath(path.ios, `in` = constraint.values) + is WhereConstraint.NotInArray -> FIRFilter.filterWhereFieldPath(path.ios, notIn = constraint.values) } } diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt index a9fe0014d..4a95a6610 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt @@ -2,8 +2,8 @@ package dev.gitlive.firebase.firestore.internal import cocoapods.FirebaseFirestoreInternal.FIRTransaction import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.ios +import dev.gitlive.firebase.firestore.toEncodedMap import dev.gitlive.firebase.internal.EncodedObject import dev.gitlive.firebase.internal.ios @@ -22,19 +22,11 @@ internal actual class NativeTransactionWrapper actual constructor(actual val nat actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeTransactionWrapper = native.updateData(encodedData.ios, documentRef.ios).let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, + encodedFieldsAndValues: List, ): NativeTransactionWrapper = native.updateData( - encodedFieldsAndValues.toMap(), - documentRef.ios, - ).let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ): NativeTransactionWrapper = native.updateData( - encodedFieldsAndValues.toMap(), + encodedFieldsAndValues.toEncodedMap(), documentRef.ios, ).let { this } diff --git a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt index f1551cecf..ef8899553 100644 --- a/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt +++ b/firebase-firestore/src/iosMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt @@ -1,10 +1,10 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeWriteBatch import dev.gitlive.firebase.firestore.await import dev.gitlive.firebase.firestore.ios +import dev.gitlive.firebase.firestore.toEncodedMap import dev.gitlive.firebase.internal.EncodedObject import dev.gitlive.firebase.internal.ios @@ -23,19 +23,11 @@ internal actual class NativeWriteBatchWrapper actual constructor(actual val nati actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeWriteBatchWrapper = native.updateData(encodedData.ios, documentRef.ios).let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, + encodedFieldsAndValues: List, ): NativeWriteBatchWrapper = native.updateData( - encodedFieldsAndValues.toMap(), - documentRef.ios, - ).let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ): NativeWriteBatchWrapper = native.updateData( - encodedFieldsAndValues.toMap(), + encodedFieldsAndValues.toEncodedMap(), documentRef.ios, ).let { this } diff --git a/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt b/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt index e7c6fc04f..494225234 100644 --- a/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt +++ b/firebase-firestore/src/iosTest/kotlin/dev/gitlive/firebase/firestore/ContextSwitchTest.kt @@ -121,28 +121,38 @@ class ContextSwitchTest { }, ) { data -> - fun getDocument() = firestore.collection("fieldValuesOps") + val doc = firestore.collection("fieldValuesOps") .document("fieldValuesOps") - // store - getDocument().set(strategy = TestData.serializer(), data = TestData(data.initial), merge = false) - - // append & verify - getDocument().update(data.updates[0].op) - - var savedData = getDocument().get().data(TestData.serializer()) - assertEquals(data.updates[0].expected, savedData.values) - - // remove & verify - getDocument().update(data.updates[1].op) - savedData = getDocument().get().data(TestData.serializer()) - assertEquals(data.updates[1].expected, savedData.values) + try { + // store + doc.set( + strategy = TestData.serializer(), + data = TestData(data.initial), + merge = false, + ) - val list = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) - assertEquals(data.updates[1].expected, list) - // delete & verify - getDocument().update(data.updates[2].op) - val deletedList = getDocument().get().get(TestData::values.name, ListSerializer(Int.serializer()).nullable) - assertEquals(data.updates[2].expected, deletedList) + // append & verify + doc.update(data.updates[0].op) + + var savedData = doc.get().data(TestData.serializer()) + assertEquals(data.updates[0].expected, savedData.values) + + // remove & verify + doc.update(data.updates[1].op) + savedData = doc.get().data(TestData.serializer()) + assertEquals(data.updates[1].expected, savedData.values) + + val list = doc.get() + .get(TestData::values.name, ListSerializer(Int.serializer()).nullable) + assertEquals(data.updates[1].expected, list) + // delete & verify + doc.update(data.updates[2].op) + val deletedList = doc.get() + .get(TestData::values.name, ListSerializer(Int.serializer()).nullable) + assertEquals(data.updates[2].expected, deletedList) + } finally { + doc.delete() + } } } diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt index 5065defa2..ac72e14af 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeDocumentReference.kt @@ -1,6 +1,5 @@ package dev.gitlive.firebase.firestore.internal -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeCollectionReference import dev.gitlive.firebase.firestore.NativeDocumentReferenceType import dev.gitlive.firebase.firestore.NativeDocumentSnapshot @@ -74,25 +73,21 @@ internal actual class NativeDocumentReference actual constructor(actual val nati ).await() } - actual suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List>) { + actual suspend fun updateEncoded(encodedFieldsAndValues: List) { rethrow { encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() } - ?.performUpdate { field, value, moreFieldsAndValues -> - updateDoc(js, field, value, *moreFieldsAndValues) - } + ?.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + updateDoc(js, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + updateDoc(js, fieldPath, value, *moreFieldsAndValues) + }, + ) ?.await() } } - actual suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List>) { - rethrow { - encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() } - ?.performUpdate { field, value, moreFieldsAndValues -> - updateDoc(js, field, value, *moreFieldsAndValues) - }?.await() - } - } - actual suspend fun delete() = rethrow { deleteDoc(js).await() } override fun equals(other: Any?): Boolean = diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt index bc49b3437..04327dfd1 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeQueryWrapper.kt @@ -45,17 +45,17 @@ internal actual open class NativeQueryWrapper internal actual constructor(actual is Filter.Or -> or(*filters.map { it.toQueryConstraint() }.toTypedArray()) is Filter.Field -> { val value = when (constraint) { - is WhereConstraint.ForNullableObject -> constraint.safeValue - is WhereConstraint.ForObject -> constraint.safeValue - is WhereConstraint.ForArray -> constraint.safeValues.toTypedArray() + is WhereConstraint.ForNullableObject -> constraint.value + is WhereConstraint.ForObject -> constraint.value + is WhereConstraint.ForArray -> constraint.values.toTypedArray() } dev.gitlive.firebase.firestore.externals.where(field, constraint.filterOp, value) } is Filter.Path -> { val value = when (constraint) { - is WhereConstraint.ForNullableObject -> constraint.safeValue - is WhereConstraint.ForObject -> constraint.safeValue - is WhereConstraint.ForArray -> constraint.safeValues.toTypedArray() + is WhereConstraint.ForNullableObject -> constraint.value + is WhereConstraint.ForObject -> constraint.value + is WhereConstraint.ForArray -> constraint.values.toTypedArray() } dev.gitlive.firebase.firestore.externals.where(path.js, constraint.filterOp, value) } diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt index 59ba9896d..f12b9d637 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeTransactionWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeTransaction import dev.gitlive.firebase.firestore.externals.Transaction import dev.gitlive.firebase.firestore.js @@ -29,22 +28,18 @@ internal actual class NativeTransactionWrapper internal actual constructor(actua actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeTransactionWrapper = rethrow { js.update(documentRef.js, encodedData.js) } .let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, + encodedFieldsAndValues: List, ): NativeTransactionWrapper = rethrow { - encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - js.update(documentRef.js, field, value, *moreFieldsAndValues) - } - }.let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ): NativeTransactionWrapper = rethrow { - encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - js.update(documentRef.js, field, value, *moreFieldsAndValues) - } + encodedFieldsAndValues.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + js.update(documentRef.js, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + js.update(documentRef.js, fieldPath, value, *moreFieldsAndValues) + }, + ) }.let { this } actual fun delete(documentRef: DocumentReference) = diff --git a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt index e97cc31ab..45515bdcc 100644 --- a/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt +++ b/firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/internal/NativeWriteBatchWrapper.kt @@ -1,7 +1,6 @@ package dev.gitlive.firebase.firestore.internal import dev.gitlive.firebase.firestore.DocumentReference -import dev.gitlive.firebase.firestore.EncodedFieldPath import dev.gitlive.firebase.firestore.NativeWriteBatch import dev.gitlive.firebase.firestore.externals.WriteBatch import dev.gitlive.firebase.firestore.js @@ -26,22 +25,18 @@ internal actual class NativeWriteBatchWrapper internal actual constructor(actual actual fun updateEncoded(documentRef: DocumentReference, encodedData: EncodedObject): NativeWriteBatchWrapper = rethrow { js.update(documentRef.js, encodedData.js) } .let { this } - actual fun updateEncodedFieldsAndValues( + actual fun updateEncoded( documentRef: DocumentReference, - encodedFieldsAndValues: List>, + encodedFieldsAndValues: List, ): NativeWriteBatchWrapper = rethrow { - encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - js.update(documentRef.js, field, value, *moreFieldsAndValues) - } - }.let { this } - - actual fun updateEncodedFieldPathsAndValues( - documentRef: DocumentReference, - encodedFieldsAndValues: List>, - ): NativeWriteBatchWrapper = rethrow { - encodedFieldsAndValues.performUpdate { field, value, moreFieldsAndValues -> - js.update(documentRef.js, field, value, *moreFieldsAndValues) - } + encodedFieldsAndValues.performUpdate( + updateAsField = { field, value, moreFieldsAndValues -> + js.update(documentRef.js, field, value, *moreFieldsAndValues) + }, + updateAsFieldPath = { fieldPath, value, moreFieldsAndValues -> + js.update(documentRef.js, fieldPath, value, *moreFieldsAndValues) + }, + ) }.let { this } actual fun delete(documentRef: DocumentReference) = diff --git a/firebase-functions/package.json b/firebase-functions/package.json index ed25dbf4e..dc133b64a 100644 --- a/firebase-functions/package.json +++ b/firebase-functions/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-functions", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-functions.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4" diff --git a/firebase-installations/package.json b/firebase-installations/package.json index bd98df6a8..80219d7c4 100644 --- a/firebase-installations/package.json +++ b/firebase-installations/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-installations", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-installations.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4" diff --git a/firebase-messaging/package.json b/firebase-messaging/package.json index 057b92487..f36b43519 100644 --- a/firebase-messaging/package.json +++ b/firebase-messaging/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-messaging", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-messaging.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.8.20", "kotlinx-coroutines-core": "1.6.4" diff --git a/firebase-perf/package.json b/firebase-perf/package.json index cd6e77684..e52cbc210 100644 --- a/firebase-perf/package.json +++ b/firebase-perf/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-perf", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-perf.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.6.10", "kotlinx-coroutines-core": "1.6.1-native-mt" diff --git a/firebase-storage/api/android/firebase-storage.api b/firebase-storage/api/android/firebase-storage.api index 4715f8dcf..3613f5aab 100644 --- a/firebase-storage/api/android/firebase-storage.api +++ b/firebase-storage/api/android/firebase-storage.api @@ -13,6 +13,7 @@ public final class dev/gitlive/firebase/storage/FirebaseStorage { public final fun getMaxOperationRetryTime-UwyO8pc ()J public final fun getMaxUploadRetryTime-UwyO8pc ()J public final fun getReference ()Ldev/gitlive/firebase/storage/StorageReference; + public final fun getReferenceFromUrl (Ljava/lang/String;)Ldev/gitlive/firebase/storage/StorageReference; public final fun reference (Ljava/lang/String;)Ldev/gitlive/firebase/storage/StorageReference; public final fun setMaxOperationRetryTime-LRDsOJo (J)V public final fun setMaxUploadRetryTime-LRDsOJo (J)V diff --git a/firebase-storage/api/jvm/firebase-storage.api b/firebase-storage/api/jvm/firebase-storage.api index e94fb4ab9..eaa50149c 100644 --- a/firebase-storage/api/jvm/firebase-storage.api +++ b/firebase-storage/api/jvm/firebase-storage.api @@ -11,6 +11,7 @@ public final class dev/gitlive/firebase/storage/FirebaseStorage { public final fun getMaxOperationRetryTime-UwyO8pc ()J public final fun getMaxUploadRetryTime-UwyO8pc ()J public final fun getReference ()Ldev/gitlive/firebase/storage/StorageReference; + public final fun getReferenceFromUrl (Ljava/lang/String;)Ldev/gitlive/firebase/storage/StorageReference; public final fun reference (Ljava/lang/String;)Ldev/gitlive/firebase/storage/StorageReference; public final fun setMaxOperationRetryTime-LRDsOJo (J)V public final fun setMaxUploadRetryTime-LRDsOJo (J)V diff --git a/firebase-storage/package.json b/firebase-storage/package.json index 07e46118b..6261c6a37 100644 --- a/firebase-storage/package.json +++ b/firebase-storage/package.json @@ -1,6 +1,6 @@ { "name": "@gitlive/firebase-storage", - "version": "2.0.0", + "version": "2.2.0-SNAPSHOT", "description": "Wrapper around firebase for usage in Kotlin Multiplatform projects", "main": "firebase-storage.js", "scripts": { @@ -23,7 +23,7 @@ }, "homepage": "https://github.com/GitLiveApp/firebase-kotlin-sdk", "dependencies": { - "@gitlive/firebase-app": "2.0.0", + "@gitlive/firebase-app": "2.2.0-SNAPSHOT", "firebase": "9.19.1", "kotlin": "1.6.10", "kotlinx-coroutines-core": "1.6.1-native-mt" diff --git a/firebase-storage/src/androidMain/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/androidMain/kotlin/dev/gitlive/firebase/storage/storage.kt index 8d435afed..41c906c45 100644 --- a/firebase-storage/src/androidMain/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/androidMain/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -55,6 +55,8 @@ public actual class FirebaseStorage(internal val android: com.google.firebase.st public actual val reference: StorageReference get() = StorageReference(android.reference) public actual fun reference(location: String): StorageReference = StorageReference(android.getReference(location)) + + public actual fun getReferenceFromUrl(fullUrl: String): StorageReference = StorageReference(android.getReferenceFromUrl(fullUrl)) } public val StorageReference.android: com.google.firebase.storage.StorageReference get() = android diff --git a/firebase-storage/src/commonMain/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/commonMain/kotlin/dev/gitlive/firebase/storage/storage.kt index 8485adc51..57d311fed 100644 --- a/firebase-storage/src/commonMain/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/commonMain/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -86,6 +86,16 @@ public expect class FirebaseStorage { * @return An instance of [StorageReference] at the given child path. */ public fun reference(location: String): StorageReference + + /** + * Creates a [StorageReference] given a gs:// or https:// URL pointing to a Firebase Storage location. + * + * @param fullUrl A gs:// or http[s]:// URL used to initialize the reference. For example, you can pass + * in a download URL retrieved from getDownloadUrl or the uri retrieved from toString An error is + * thrown if fullUrl is not associated with the FirebaseApp used to initialize this FirebaseStorage. + * @return An instance of [StorageReference] at the given url. + */ + public fun getReferenceFromUrl(fullUrl: String): StorageReference } @Deprecated("Deprecated to use Kotlin Duration", replaceWith = ReplaceWith("maxOperationRetryTime")) diff --git a/firebase-storage/src/iosMain/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/iosMain/kotlin/dev/gitlive/firebase/storage/storage.kt index 0a0c1afab..46dcbe97e 100644 --- a/firebase-storage/src/iosMain/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/iosMain/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -67,6 +67,8 @@ public actual class FirebaseStorage(internal val ios: FIRStorage) { public actual val reference: StorageReference get() = StorageReference(ios.reference()) public actual fun reference(location: String): StorageReference = StorageReference(ios.referenceWithPath(location)) + + public actual fun getReferenceFromUrl(fullUrl: String): StorageReference = StorageReference(ios.referenceForURL(fullUrl)) } public val StorageReference.ios: FIRStorageReference get() = ios diff --git a/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/storage.kt b/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/storage.kt index 14a7897b3..5337601ae 100644 --- a/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/storage.kt +++ b/firebase-storage/src/jsMain/kotlin/dev/gitlive/firebase/storage/storage.kt @@ -51,6 +51,8 @@ public actual class FirebaseStorage(internal val js: dev.gitlive.firebase.storag public actual val reference: StorageReference get() = StorageReference(ref(js)) public actual fun reference(location: String): StorageReference = rethrow { StorageReference(ref(js, location)) } + + public actual fun getReferenceFromUrl(fullUrl: String): StorageReference = rethrow { StorageReference(ref(js, fullUrl)) } } public val StorageReference.js get() = js diff --git a/firebase-storage/src/jvmMain/kotlin/dev/gitlive/firebase/storage/storage.jvm.kt b/firebase-storage/src/jvmMain/kotlin/dev/gitlive/firebase/storage/storage.jvm.kt index 9d4f16ab8..c3674a728 100644 --- a/firebase-storage/src/jvmMain/kotlin/dev/gitlive/firebase/storage/storage.jvm.kt +++ b/firebase-storage/src/jvmMain/kotlin/dev/gitlive/firebase/storage/storage.jvm.kt @@ -37,6 +37,10 @@ public actual class FirebaseStorage { public actual fun reference(location: String): StorageReference { TODO("Not yet implemented") } + + public actual fun getReferenceFromUrl(fullUrl: String): StorageReference { + TODO("Not yet implemented") + } } public actual class StorageReference { diff --git a/gradle.properties b/gradle.properties index 01a9fd1fc..8963c368b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -70,17 +70,17 @@ firebase-perf.skipJsTests=false firebase-storage.skipJsTests=false # Versions: -firebase-analytics.version=2.0.0 -firebase-app.version=2.0.0 -firebase-auth.version=2.0.0 -firebase-common-internal.version=2.0.0 -firebase-common.version=2.0.0 -firebase-config.version=2.0.0 -firebase-crashlytics.version=2.0.0 -firebase-database.version=2.0.0 -firebase-firestore.version=2.0.0 -firebase-functions.version=2.0.0 -firebase-installations.version=2.0.0 -firebase-messaging.version=2.0.0 -firebase-perf.version=2.0.0 -firebase-storage.version=2.0.0 +firebase-analytics.version=2.2.0-SNAPSHOT +firebase-app.version=2.2.0-SNAPSHOT +firebase-auth.version=2.2.0-SNAPSHOT +firebase-common-internal.version=2.2.0-SNAPSHOT +firebase-common.version=2.2.0-SNAPSHOT +firebase-config.version=2.2.0-SNAPSHOT +firebase-crashlytics.version=2.2.0-SNAPSHOT +firebase-database.version=2.2.0-SNAPSHOT +firebase-firestore.version=2.2.0-SNAPSHOT +firebase-functions.version=2.2.0-SNAPSHOT +firebase-installations.version=2.2.0-SNAPSHOT +firebase-messaging.version=2.2.0-SNAPSHOT +firebase-perf.version=2.2.0-SNAPSHOT +firebase-storage.version=2.2.0-SNAPSHOT diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9561276bc..df03f9ca0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,26 +1,25 @@ [versions] -agp = "8.5.2" +agp = "8.6.0" androidx-test-core = "1.6.1" androidx-test-junit = "1.2.1" androidx-test-runner = "1.6.2" ben-manes-versions = "0.51.0" firebase-bom = "33.2.0" -gitlive-firebase-java-sdk = "0.4.5" +gitlive-firebase-java-sdk = "0.4.6" gson = "2.11.0" junit = "4.13.2" kotlin = "2.0.20" kotlinx-coroutines = "1.9.0-RC.2" -kotlinx-serialization = "1.7.1" +kotlinx-serialization = "1.7.2" kotlinx-binarycompatibilityvalidator = "0.16.3" kotlinx-datetime = "0.6.1" kotlinter = "4.4.1" settings-api = "2.0" settings-language = "2.0" -firebase-cocoapods = "10.28.0" +firebase-cocoapods = "11.1.0" ios-deploymentTarget = "13.0" test-logger-plugin = "4.0.0" dokka = "1.9.20" -desugar-libs = "2.1.0" [libraries] android-gradle-plugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } @@ -50,7 +49,6 @@ kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines- kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx-serialization" } kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" } dokka-base = { module = "org.jetbrains.dokka:dokka-base", version.ref = "dokka" } -android-desugarjdk = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar-libs" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }