From 63d290a35e9b71caeb192cf38660ee7fca01aa48 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Tue, 6 Jul 2021 17:54:00 +0100 Subject: [PATCH 01/27] Add database runTransaction expect and js externals --- .../src/jsMain/kotlin/dev/gitlive/firebase/externals.kt | 5 +++++ .../kotlin/dev/gitlive/firebase/database/database.kt | 2 ++ 2 files changed, 7 insertions(+) diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt index 1c3c683f0..38444d4c7 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt @@ -317,6 +317,11 @@ external object firebase { fun update(value: Any?): Promise fun set(value: Any?): Promise fun push(): ThenableReference + fun runTransaction( + transactionUpdate: (currentData: T) -> T, + onComplete: (error: Error?, committed: Boolean, snapshot: DataSnapshot?) -> Unit, + applyLocally: Boolean? + ): Promise } open class DataSnapshot { diff --git a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt index c3b532c4d..ba041f9b4 100644 --- a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -71,6 +71,8 @@ expect class DatabaseReference : Query { suspend fun setValue(strategy: SerializationStrategy, value: T, encodeDefaults: Boolean = true) suspend fun updateChildren(update: Map, encodeDefaults: Boolean = true) suspend fun removeValue() + + suspend fun runTransaction(strategy: KSerializer, transactionUpdate: (currentData: T) -> T): DataSnapshot } expect class DataSnapshot { From b5a0b3035a809ea1af4ae321f8944233f14f45b0 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Tue, 6 Jul 2021 17:55:11 +0100 Subject: [PATCH 02/27] Add runTransaction methods for jvm, ios and js --- .../dev/gitlive/firebase/database/database.kt | 31 ++++++++++++++++--- .../dev/gitlive/firebase/database/database.kt | 19 ++++++++++++ .../dev/gitlive/firebase/database/database.kt | 18 +++++++++++ 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt index e92f95dd3..e2699628f 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -5,15 +5,14 @@ package dev.gitlive.firebase.database import com.google.android.gms.tasks.Task -import com.google.firebase.database.ChildEventListener -import com.google.firebase.database.Logger +import com.google.firebase.database.* import com.google.firebase.database.ServerValue -import com.google.firebase.database.ValueEventListener import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.FirebaseApp import dev.gitlive.firebase.database.ChildEvent.Type import dev.gitlive.firebase.decode import dev.gitlive.firebase.safeOffer +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.coroutineScope @@ -21,10 +20,12 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.produceIn +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.selects.select import kotlinx.coroutines.tasks.asDeferred import kotlinx.coroutines.tasks.await import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy @PublishedApi @@ -188,8 +189,30 @@ actual class DatabaseReference internal constructor( actual suspend fun removeValue() = android.removeValue() .run { if(persistenceEnabled) await() else awaitWhileOnline() } .run { Unit } -} + actual suspend fun runTransaction(strategy: KSerializer, transactionUpdate: (currentData: T) -> T): DataSnapshot { + val deferred = CompletableDeferred>() + android.runTransaction(object : Transaction.Handler { + + override fun doTransaction(currentData: MutableData) = + Transaction.success(transactionUpdate(decode(strategy, currentData)) as MutableData) + + override fun onComplete( + error: DatabaseError?, + committed: Boolean, + snapshot: com.google.firebase.database.DataSnapshot? + ) { + if (error == null && snapshot != null) { + deferred.complete(Result.success(DataSnapshot(snapshot))) + } else { + deferred.complete(Result.failure(Throwable(error?.message))) + } + } + + }) + return deferred.await().getOrThrow() + } +} @Suppress("UNCHECKED_CAST") actual class DataSnapshot internal constructor(val android: com.google.firebase.database.DataSnapshot) { diff --git a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt index 9ba833dc6..92c6e9553 100644 --- a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -22,6 +22,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.produceIn import kotlinx.coroutines.selects.select import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy import platform.Foundation.* import kotlin.collections.component1 @@ -156,6 +157,24 @@ actual class DatabaseReference internal constructor( actual suspend fun removeValue() { ios.await(persistenceEnabled) { removeValueWithCompletionBlock(it) } } + + actual suspend fun runTransaction(strategy: KSerializer, transactionUpdate: (currentData: T) -> T): DataSnapshot { + val deferred = CompletableDeferred>() + ios.runTransactionBlock( + block = { firMutableData -> + FIRTransactionResult.successWithValue(transactionUpdate(decode(strategy, firMutableData)) as FIRMutableData) + }, + andCompletionBlock = { error, _, snapshot -> + if (error == null) { + deferred.complete(Result.success(DataSnapshot(snapshot!!))) + } else { + deferred.complete(Result.failure(Throwable(error.localizedDescription))) + } + }, + withLocalEvents = false + ) + return deferred.await().getOrThrow() + } } @Suppress("UNCHECKED_CAST") diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt index a4f9898b6..3f7488c52 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -12,6 +12,7 @@ import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.produceIn import kotlinx.coroutines.selects.select import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy import kotlin.js.Promise @@ -127,6 +128,23 @@ actual class DatabaseReference internal constructor(override val js: firebase.da actual suspend fun setValue(strategy: SerializationStrategy, value: T, encodeDefaults: Boolean) = rethrow { js.set(encode(strategy, value, encodeDefaults)).awaitWhileOnline() } + + actual suspend fun runTransaction(strategy: KSerializer, transactionUpdate: (currentData: T) -> T): DataSnapshot { + val deferred = CompletableDeferred>() + js.runTransaction( + transactionUpdate, + { error, _, snapshot -> + if (error != null) { + deferred.complete(Result.success(DataSnapshot(snapshot!!))) + } else { + deferred.complete(Result.failure(Throwable(error?.message))) + } + }, + applyLocally = false + ).await() + return deferred.await().getOrThrow() + } + } actual class DataSnapshot internal constructor(val js: firebase.database.DataSnapshot) { From 0b8447052731c528da0ffc9c2006041ce58a8619 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Wed, 14 Jul 2021 17:27:06 +0100 Subject: [PATCH 03/27] Add serialization plugin to database build --- firebase-database/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/firebase-database/build.gradle.kts b/firebase-database/build.gradle.kts index 0010e05e2..59ca18981 100644 --- a/firebase-database/build.gradle.kts +++ b/firebase-database/build.gradle.kts @@ -9,6 +9,7 @@ version = project.property("firebase-database.version") as String plugins { id("com.android.library") kotlin("multiplatform") + kotlin("plugin.serialization") version "1.5.10" } repositories { From 1536e4205d2f12baa325b49c7868ef4fb5d70e2b Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Thu, 15 Jul 2021 09:55:54 +0100 Subject: [PATCH 04/27] Fix KSerialiser --- .../commonMain/kotlin/dev/gitlive/firebase/database/database.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt index ba041f9b4..afec518e9 100644 --- a/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -9,6 +9,7 @@ import dev.gitlive.firebase.FirebaseApp import dev.gitlive.firebase.database.ChildEvent.Type.* import kotlinx.coroutines.flow.Flow import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy /** Returns the [FirebaseDatabase] instance of the default [FirebaseApp]. */ From 8b63096183eb14eab7ae6d0c231b85c81d9c483f Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Mon, 19 Jul 2021 09:27:09 +0100 Subject: [PATCH 05/27] Fix error thrown --- .../kotlin/dev/gitlive/firebase/database/database.kt | 6 +++--- .../kotlin/dev/gitlive/firebase/database/database.kt | 6 +++--- .../jsMain/kotlin/dev/gitlive/firebase/database/database.kt | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt index e2699628f..ec59d6895 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -202,10 +202,10 @@ actual class DatabaseReference internal constructor( committed: Boolean, snapshot: com.google.firebase.database.DataSnapshot? ) { - if (error == null && snapshot != null) { - deferred.complete(Result.success(DataSnapshot(snapshot))) + if(error != null) { + throw error.toException() } else { - deferred.complete(Result.failure(Throwable(error?.message))) + deferred.complete(Result.success(DataSnapshot(snapshot!!))) } } diff --git a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt index 92c6e9553..f6ce2560c 100644 --- a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -165,10 +165,10 @@ actual class DatabaseReference internal constructor( FIRTransactionResult.successWithValue(transactionUpdate(decode(strategy, firMutableData)) as FIRMutableData) }, andCompletionBlock = { error, _, snapshot -> - if (error == null) { - deferred.complete(Result.success(DataSnapshot(snapshot!!))) + if (error != null) { + throw error } else { - deferred.complete(Result.failure(Throwable(error.localizedDescription))) + deferred.complete(Result.success(DataSnapshot(snapshot!!))) } }, withLocalEvents = false diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt index 3f7488c52..da453b581 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -135,9 +135,9 @@ actual class DatabaseReference internal constructor(override val js: firebase.da transactionUpdate, { error, _, snapshot -> if (error != null) { - deferred.complete(Result.success(DataSnapshot(snapshot!!))) + throw error } else { - deferred.complete(Result.failure(Throwable(error?.message))) + deferred.complete(Result.success(DataSnapshot(snapshot!!))) } }, applyLocally = false From 900426bb9e8cc976b05426e839ab8797109ebaf6 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Mon, 19 Jul 2021 09:41:22 +0100 Subject: [PATCH 06/27] Fix ios error thrown --- .../iosMain/kotlin/dev/gitlive/firebase/database/database.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt index f6ce2560c..6d900aa39 100644 --- a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -166,7 +166,7 @@ actual class DatabaseReference internal constructor( }, andCompletionBlock = { error, _, snapshot -> if (error != null) { - throw error + throw DatabaseException(error.toString(), null) } else { deferred.complete(Result.success(DataSnapshot(snapshot!!))) } From c6fb31bf32c81b2d563cb9b3ae0dc631164af3f4 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Mon, 19 Jul 2021 12:55:03 +0100 Subject: [PATCH 07/27] Remove Result type --- .../kotlin/dev/gitlive/firebase/database/database.kt | 8 ++++---- .../kotlin/dev/gitlive/firebase/database/database.kt | 8 ++++---- .../kotlin/dev/gitlive/firebase/database/database.kt | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt index ec59d6895..e3132ad52 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -191,7 +191,7 @@ actual class DatabaseReference internal constructor( .run { Unit } actual suspend fun runTransaction(strategy: KSerializer, transactionUpdate: (currentData: T) -> T): DataSnapshot { - val deferred = CompletableDeferred>() + val deferred = CompletableDeferred() android.runTransaction(object : Transaction.Handler { override fun doTransaction(currentData: MutableData) = @@ -203,14 +203,14 @@ actual class DatabaseReference internal constructor( snapshot: com.google.firebase.database.DataSnapshot? ) { if(error != null) { - throw error.toException() + deferred.completeExceptionally(error.toException()) } else { - deferred.complete(Result.success(DataSnapshot(snapshot!!))) + deferred.complete(DataSnapshot(snapshot!!)) } } }) - return deferred.await().getOrThrow() + return deferred.await() } } @Suppress("UNCHECKED_CAST") diff --git a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt index 6d900aa39..c7c68745c 100644 --- a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -159,21 +159,21 @@ actual class DatabaseReference internal constructor( } actual suspend fun runTransaction(strategy: KSerializer, transactionUpdate: (currentData: T) -> T): DataSnapshot { - val deferred = CompletableDeferred>() + val deferred = CompletableDeferred() ios.runTransactionBlock( block = { firMutableData -> FIRTransactionResult.successWithValue(transactionUpdate(decode(strategy, firMutableData)) as FIRMutableData) }, andCompletionBlock = { error, _, snapshot -> if (error != null) { - throw DatabaseException(error.toString(), null) + deferred.completeExceptionally(DatabaseException(error.toString(), null)) } else { - deferred.complete(Result.success(DataSnapshot(snapshot!!))) + deferred.complete(DataSnapshot(snapshot!!)) } }, withLocalEvents = false ) - return deferred.await().getOrThrow() + return deferred.await() } } diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt index da453b581..561359196 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -130,19 +130,19 @@ actual class DatabaseReference internal constructor(override val js: firebase.da rethrow { js.set(encode(strategy, value, encodeDefaults)).awaitWhileOnline() } actual suspend fun runTransaction(strategy: KSerializer, transactionUpdate: (currentData: T) -> T): DataSnapshot { - val deferred = CompletableDeferred>() + val deferred = CompletableDeferred() js.runTransaction( transactionUpdate, { error, _, snapshot -> if (error != null) { - throw error + deferred.completeExceptionally(error) } else { - deferred.complete(Result.success(DataSnapshot(snapshot!!))) + deferred.complete(DataSnapshot(snapshot!!)) } }, applyLocally = false ).await() - return deferred.await().getOrThrow() + return deferred.await() } } From 50e33d68cb35fedf593d391df158ff5e2e37696d Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Fri, 6 Aug 2021 09:53:44 +0100 Subject: [PATCH 08/27] Add delete app method --- .../kotlin/dev/gitlive/firebase/firebase.kt | 2 ++ .../kotlin/dev/gitlive/firebase/firebase.kt | 1 + .../kotlin/dev/gitlive/firebase/firebase.kt | 14 ++++++++++++++ .../jsMain/kotlin/dev/gitlive/firebase/firebase.kt | 2 ++ .../kotlin/dev/gitlive/firebase/externals.kt | 1 + 5 files changed, 20 insertions(+) diff --git a/firebase-app/src/androidMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/androidMain/kotlin/dev/gitlive/firebase/firebase.kt index ca86a96ee..6b43c3c07 100644 --- a/firebase-app/src/androidMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/androidMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -34,6 +34,8 @@ actual class FirebaseApp internal constructor(val android: com.google.firebase.F get() = android.name actual val options: FirebaseOptions get() = android.options.run { FirebaseOptions(applicationId, apiKey, databaseUrl, gaTrackingId, storageBucket, projectId) } + + actual suspend fun delete() = android.delete() } actual fun Firebase.apps(context: Any?) = com.google.firebase.FirebaseApp.getApps(context as Context) diff --git a/firebase-app/src/commonMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/commonMain/kotlin/dev/gitlive/firebase/firebase.kt index 871b60b59..a02e952c9 100644 --- a/firebase-app/src/commonMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/commonMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -19,6 +19,7 @@ object Firebase expect class FirebaseApp { val name: String val options: FirebaseOptions + suspend fun delete() } /** Returns the default firebase app instance. */ diff --git a/firebase-app/src/iosMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/iosMain/kotlin/dev/gitlive/firebase/firebase.kt index b4e6819ef..a01166c02 100644 --- a/firebase-app/src/iosMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/iosMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -5,6 +5,7 @@ package dev.gitlive.firebase import cocoapods.FirebaseCore.* +import kotlinx.coroutines.CompletableDeferred actual open class FirebaseException(message: String) : Exception(message) actual open class FirebaseNetworkException(message: String) : FirebaseException(message) @@ -31,6 +32,19 @@ actual class FirebaseApp internal constructor(val ios: FIRApp) { get() = ios.name actual val options: FirebaseOptions get() = ios.options.run { FirebaseOptions(bundleID, APIKey!!, databaseURL!!, trackingID, storageBucket, projectID) } + + actual suspend fun delete() { + val job = CompletableDeferred() + val result = ios.deleteApp { error -> + if(error == null) { + job.complete(Unit) + } else { + job.completeExceptionally(error.toException()) + } + } + job.await() + return result + } } actual fun Firebase.apps(context: Any?) = FIRApp.allApps() diff --git a/firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt index 16214a1c4..ea576f17e 100644 --- a/firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -28,6 +28,8 @@ actual class FirebaseApp internal constructor(val js: firebase.App) { get() = js.options.run { FirebaseOptions(applicationId, apiKey, databaseUrl, gaTrackingId, storageBucket, projectId, messagingSenderId, authDomain) } + + actual suspend fun delete() = js.delete() } actual fun Firebase.apps(context: Any?) = firebase.apps.map { FirebaseApp(it) } diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt index 0648f301a..074567b64 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt @@ -15,6 +15,7 @@ external object firebase { open class App { val name: String val options: Options + fun delete() fun functions(region: String? = definedExternally): functions.Functions fun database(url: String? = definedExternally): database.Database fun firestore(): firestore.Firestore From fcc97ce7e5092f68c5520b1ab0cf93bed5a97082 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Fri, 6 Aug 2021 09:54:13 +0100 Subject: [PATCH 09/27] Add database emulator and rules --- test/database.rules.json | 6 ++++++ test/firebase.json | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 test/database.rules.json diff --git a/test/database.rules.json b/test/database.rules.json new file mode 100644 index 000000000..b104e9c24 --- /dev/null +++ b/test/database.rules.json @@ -0,0 +1,6 @@ +{ + "rules": { + ".read": true, + ".write": true + } +} \ No newline at end of file diff --git a/test/firebase.json b/test/firebase.json index fb30d3850..eaadee2f0 100644 --- a/test/firebase.json +++ b/test/firebase.json @@ -2,12 +2,18 @@ "firestore": { "rules": "firestore.rules" }, + "database": { + "rules": "database.rules.json" + }, "emulators": { "auth": { "port": 9099 }, "firestore": { "port": 8080 + }, + "database": { + "port": 9000 } } } From 515b7da24f742a7cabd3eff4b81a997416b4d2a4 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Fri, 6 Aug 2021 10:22:19 +0100 Subject: [PATCH 10/27] Remove suspend --- .../kotlin/dev/gitlive/firebase/firebase.kt | 2 +- .../kotlin/dev/gitlive/firebase/firebase.kt | 2 +- .../iosMain/kotlin/dev/gitlive/firebase/firebase.kt | 13 +------------ .../jsMain/kotlin/dev/gitlive/firebase/firebase.kt | 2 +- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/firebase-app/src/androidMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/androidMain/kotlin/dev/gitlive/firebase/firebase.kt index 6b43c3c07..0527e2e92 100644 --- a/firebase-app/src/androidMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/androidMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -35,7 +35,7 @@ actual class FirebaseApp internal constructor(val android: com.google.firebase.F actual val options: FirebaseOptions get() = android.options.run { FirebaseOptions(applicationId, apiKey, databaseUrl, gaTrackingId, storageBucket, projectId) } - actual suspend fun delete() = android.delete() + actual fun delete() = android.delete() } actual fun Firebase.apps(context: Any?) = com.google.firebase.FirebaseApp.getApps(context as Context) diff --git a/firebase-app/src/commonMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/commonMain/kotlin/dev/gitlive/firebase/firebase.kt index a02e952c9..75e3df31d 100644 --- a/firebase-app/src/commonMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/commonMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -19,7 +19,7 @@ object Firebase expect class FirebaseApp { val name: String val options: FirebaseOptions - suspend fun delete() + fun delete() } /** Returns the default firebase app instance. */ diff --git a/firebase-app/src/iosMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/iosMain/kotlin/dev/gitlive/firebase/firebase.kt index a01166c02..c8488259f 100644 --- a/firebase-app/src/iosMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/iosMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -33,18 +33,7 @@ actual class FirebaseApp internal constructor(val ios: FIRApp) { actual val options: FirebaseOptions get() = ios.options.run { FirebaseOptions(bundleID, APIKey!!, databaseURL!!, trackingID, storageBucket, projectID) } - actual suspend fun delete() { - val job = CompletableDeferred() - val result = ios.deleteApp { error -> - if(error == null) { - job.complete(Unit) - } else { - job.completeExceptionally(error.toException()) - } - } - job.await() - return result - } + actual fun delete() { } } actual fun Firebase.apps(context: Any?) = FIRApp.allApps() diff --git a/firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt b/firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt index ea576f17e..1d911c290 100644 --- a/firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt +++ b/firebase-app/src/jsMain/kotlin/dev/gitlive/firebase/firebase.kt @@ -29,7 +29,7 @@ actual class FirebaseApp internal constructor(val js: firebase.App) { FirebaseOptions(applicationId, apiKey, databaseUrl, gaTrackingId, storageBucket, projectId, messagingSenderId, authDomain) } - actual suspend fun delete() = js.delete() + actual fun delete() = js.delete() } actual fun Firebase.apps(context: Any?) = firebase.apps.map { FirebaseApp(it) } From 872c1a12c15457e2983d3935c3cccb4f9b37b203 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Fri, 6 Aug 2021 12:18:52 +0100 Subject: [PATCH 11/27] Add database tests --- .../dev/gitlive/firebase/database/database.kt | 15 +++ .../dev/gitlive/firebase/database/database.kt | 2 - .../dev/gitlive/firebase/database/database.kt | 92 +++++++++++++++++++ .../dev/gitlive/firebase/database/database.kt | 20 ++++ .../dev/gitlive/firebase/database/database.kt | 26 ++++++ 5 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 firebase-database/src/androidAndroidTest/kotlin/dev/gitlive/firebase/database/database.kt create mode 100644 firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt create mode 100644 firebase-database/src/iosTest/kotlin/dev/gitlive/firebase/database/database.kt create mode 100644 firebase-database/src/jsTest/kotlin/dev/gitlive/firebase/database/database.kt diff --git a/firebase-database/src/androidAndroidTest/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidAndroidTest/kotlin/dev/gitlive/firebase/database/database.kt new file mode 100644 index 000000000..ebe4d120b --- /dev/null +++ b/firebase-database/src/androidAndroidTest/kotlin/dev/gitlive/firebase/database/database.kt @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:JvmName("tests") +package dev.gitlive.firebase.database + +import androidx.test.platform.app.InstrumentationRegistry +import kotlinx.coroutines.runBlocking + +actual val emulatorHost: String = "10.0.2.2" + +actual val context: Any = InstrumentationRegistry.getInstrumentation().targetContext + +actual fun runTest(test: suspend () -> Unit) = runBlocking { test() } diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt index e3132ad52..06f4e8ad0 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -13,14 +13,12 @@ import dev.gitlive.firebase.database.ChildEvent.Type import dev.gitlive.firebase.decode import dev.gitlive.firebase.safeOffer import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.produceIn -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.selects.select import kotlinx.coroutines.tasks.asDeferred import kotlinx.coroutines.tasks.await diff --git a/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt new file mode 100644 index 000000000..062052aca --- /dev/null +++ b/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt @@ -0,0 +1,92 @@ +package dev.gitlive.firebase.database + +import dev.gitlive.firebase.* +import kotlinx.coroutines.flow.first +import kotlinx.serialization.* +import kotlin.test.* + +expect val emulatorHost: String +expect val context: Any +expect fun runTest(test: suspend () -> Unit) + +class FirebaseDatabaseTest { + + @Serializable + data class DatabaseTest(val prop: String, val likes: Int = 0) + + @BeforeTest + fun initializeFirebase() { + Firebase + .takeIf { Firebase.apps(context).isEmpty() } + ?.apply { + initialize( + context, + FirebaseOptions( + applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", + apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", + databaseUrl = "http://fir-kotlin-sdk.firebaseio.com", + storageBucket = "fir-kotlin-sdk.appspot.com", + projectId = "fir-kotlin-sdk", + gcmSenderId = "846484016111" + ) + ) + Firebase.database.useEmulator(emulatorHost, 9000) + } + } + + @AfterTest + fun tearDown() { + Firebase + .takeIf { Firebase.apps(context).isNotEmpty() } + ?.apply { app.delete() } + } + + @Test + fun testBasicIncrementTransaction() = runTest { + val data = DatabaseTest("post1", 2) + val userRef = Firebase.database.reference("users/user_1/post_id_1") + setupDatabase(userRef, data, DatabaseTest.serializer()) + + // Check database before transaction + val userDocBefore = userRef.valueEvents.first().value(DatabaseTest.serializer()) + assertEquals(data.prop, userDocBefore.prop) + assertEquals(data.likes, userDocBefore.likes) + + // Run transaction + userRef.runTransaction(DatabaseTest.serializer()) { DatabaseTest(data.prop, it.likes + 1) } + + // Check the database after transaction + val userDocAfter = userRef.valueEvents.first().value(DatabaseTest.serializer()) + assertEquals(data.prop, userDocAfter.prop) + assertEquals(data.likes, userDocAfter.likes + 1) + } + + @Test + fun testBasicDecrementTransaction() = runTest { + val data = DatabaseTest("post2", 2) + val userRef = Firebase.database.reference("users/user_1/post_id_2") + setupDatabase(userRef, data, DatabaseTest.serializer()) + + // Check database before transaction + val userDocBefore = userRef.valueEvents.first().value(DatabaseTest.serializer()) + assertEquals(data.prop, userDocBefore.prop) + assertEquals(data.likes, userDocBefore.likes) + + // Run transaction + userRef.runTransaction(DatabaseTest.serializer()) { DatabaseTest(data.prop, it.likes - 1) } + + // Check the database after transaction + val userDocAfter = userRef.valueEvents.first().value(DatabaseTest.serializer()) + assertEquals(data.prop, userDocAfter.prop) + assertEquals(data.likes, userDocAfter.likes - 1) + } + + private suspend fun setupDatabase(ref: DatabaseReference, data: T, strategy: SerializationStrategy) { + try { + ref.setValue(strategy, data) + } catch (err: DatabaseException) { + println(err) + } + } + +} \ No newline at end of file diff --git a/firebase-database/src/iosTest/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosTest/kotlin/dev/gitlive/firebase/database/database.kt new file mode 100644 index 000000000..25f5ee6d3 --- /dev/null +++ b/firebase-database/src/iosTest/kotlin/dev/gitlive/firebase/database/database.kt @@ -0,0 +1,20 @@ +package dev.gitlive.firebase.database + +import kotlinx.coroutines.* +import platform.Foundation.* + +actual val emulatorHost: String = "localhost" + +actual val context: Any = Unit + +actual fun runTest(test: suspend () -> Unit) = runBlocking { + val testRun = MainScope().async { test() } + while (testRun.isActive) { + NSRunLoop.mainRunLoop.runMode( + NSDefaultRunLoopMode, + beforeDate = NSDate.create(timeInterval = 1.0, sinceDate = NSDate()) + ) + yield() + } + testRun.await() +} diff --git a/firebase-database/src/jsTest/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsTest/kotlin/dev/gitlive/firebase/database/database.kt new file mode 100644 index 000000000..e30aac335 --- /dev/null +++ b/firebase-database/src/jsTest/kotlin/dev/gitlive/firebase/database/database.kt @@ -0,0 +1,26 @@ +package dev.gitlive.firebase.database + +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise + +actual val emulatorHost: String = "localhost" + +actual val context: Any = Unit + +actual fun runTest(test: suspend () -> Unit) = GlobalScope + .promise { + try { + test() + } catch (e: Throwable) { + e.log() + throw e + } + }.asDynamic() + +internal fun Throwable.log() { + console.error(this) + cause?.let { + console.error("Caused by:") + it.log() + } +} \ No newline at end of file From 98fd19a89d7b5d6df1229bdbfc72d67ef1669d2a Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 23 Aug 2021 08:27:17 +0000 Subject: [PATCH 12/27] Fix js transaction name --- .../src/jsMain/kotlin/dev/gitlive/firebase/externals.kt | 2 +- .../src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt index 074567b64..3a058e68b 100644 --- a/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt +++ b/firebase-common/src/jsMain/kotlin/dev/gitlive/firebase/externals.kt @@ -318,7 +318,7 @@ external object firebase { fun update(value: Any?): Promise fun set(value: Any?): Promise fun push(): ThenableReference - fun runTransaction( + fun transaction( transactionUpdate: (currentData: T) -> T, onComplete: (error: Error?, committed: Boolean, snapshot: DataSnapshot?) -> Unit, applyLocally: Boolean? diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt index 561359196..595794c64 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -131,7 +131,7 @@ actual class DatabaseReference internal constructor(override val js: firebase.da actual suspend fun runTransaction(strategy: KSerializer, transactionUpdate: (currentData: T) -> T): DataSnapshot { val deferred = CompletableDeferred() - js.runTransaction( + js.transaction( transactionUpdate, { error, _, snapshot -> if (error != null) { From 1d8b9894eca247809e8d0de1e4e5c0fcf0cad84d Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 23 Aug 2021 08:27:37 +0000 Subject: [PATCH 13/27] Add env vars for emulators to actions --- .github/workflows/pull_request.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 1c687cd6f..d803081fb 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -30,6 +30,10 @@ jobs: run: ./gradlew assemble - name: Run JS Tests run: ./gradlew cleanTest jsTest + env: + FIREBASE_AUTH_EMULATOR_HOST: "localhost:9099" + FIREBASE_DATABASE_EMULATOR_HOST: "localhost:9000" + FIRESTORE_EMULATOR_HOST: "localhost:8080" - name: Upload test artifact uses: actions/upload-artifact@v2 if: failure() @@ -44,3 +48,7 @@ jobs: arch: x86_64 profile: Nexus 6 script: ./gradlew connectedAndroidTest + env: + FIREBASE_AUTH_EMULATOR_HOST: "localhost:9099" + FIREBASE_DATABASE_EMULATOR_HOST: "localhost:9000" + FIRESTORE_EMULATOR_HOST: "localhost:8080" From 59bbacf0046e51c9ef30a4596bd1eaa4b70c0f3b Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 23 Aug 2021 09:19:03 +0000 Subject: [PATCH 14/27] Use GITHUB_ENV for env vars --- .github/workflows/pull_request.yml | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 1b0d70c60..580bacd96 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -21,6 +21,11 @@ jobs: java-version: '11' - name: Grant execute permission for gradlew run: chmod +x gradlew + - name: Set environment variables for Firebase + run: | + echo "FIREBASE_AUTH_EMULATOR_HOST=localhost:9099" >> $GITHUB_ENV + echo "FIREBASE_DATABASE_EMULATOR_HOST=localhost:9000" >> $GITHUB_ENV + echo "FIRESTORE_EMULATOR_HOST=localhost:8080" >> $GITHUB_ENV - name: Install Carthage run: brew list carthage || brew install carthage - name: Install Firebase tools @@ -31,10 +36,6 @@ jobs: run: ./gradlew assemble - name: Run JS Tests run: ./gradlew cleanTest jsTest - env: - FIREBASE_AUTH_EMULATOR_HOST: "localhost:9099" - FIREBASE_DATABASE_EMULATOR_HOST: "localhost:9000" - FIRESTORE_EMULATOR_HOST: "localhost:8080" - name: Upload JS test artifact uses: actions/upload-artifact@v2 if: failure() @@ -43,10 +44,6 @@ jobs: path: "firebase-firestore/build/reports/tests/jsTest/" - name: Run iOS Tests run: ./gradlew cleanTest iosX64Test - env: - FIREBASE_AUTH_EMULATOR_HOST: "localhost:9099" - FIREBASE_DATABASE_EMULATOR_HOST: "localhost:9000" - FIRESTORE_EMULATOR_HOST: "localhost:8080" - name: Upload iOS test artifact uses: actions/upload-artifact@v2 if: failure() @@ -61,10 +58,6 @@ jobs: arch: x86_64 profile: Nexus 6 script: ./gradlew connectedAndroidTest - env: - FIREBASE_AUTH_EMULATOR_HOST: "localhost:9099" - FIREBASE_DATABASE_EMULATOR_HOST: "localhost:9000" - FIRESTORE_EMULATOR_HOST: "localhost:8080" - name: Upload Android test artifact uses: actions/upload-artifact@v2 if: failure() From b9688c9f8a30f200928f10c8a036f98407992491 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 23 Aug 2021 12:42:33 +0000 Subject: [PATCH 15/27] Move env to run level --- .github/workflows/pull_request.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 580bacd96..914a042cc 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -21,11 +21,6 @@ jobs: java-version: '11' - name: Grant execute permission for gradlew run: chmod +x gradlew - - name: Set environment variables for Firebase - run: | - echo "FIREBASE_AUTH_EMULATOR_HOST=localhost:9099" >> $GITHUB_ENV - echo "FIREBASE_DATABASE_EMULATOR_HOST=localhost:9000" >> $GITHUB_ENV - echo "FIRESTORE_EMULATOR_HOST=localhost:8080" >> $GITHUB_ENV - name: Install Carthage run: brew list carthage || brew install carthage - name: Install Firebase tools @@ -36,6 +31,8 @@ jobs: run: ./gradlew assemble - name: Run JS Tests run: ./gradlew cleanTest jsTest + env: + FIREBASE_DATABASE_EMULATOR_HOST: localhost:9000 - name: Upload JS test artifact uses: actions/upload-artifact@v2 if: failure() From 7350bb3c6caf13a19b256c9b76b0782daa9d59fa Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Tue, 24 Aug 2021 12:40:46 +0100 Subject: [PATCH 16/27] Move Firebase cleanup into function instead of AfterTest --- firebase-database/build.gradle.kts | 4 +- .../dev/gitlive/firebase/database/database.kt | 45 ++++++++++--------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/firebase-database/build.gradle.kts b/firebase-database/build.gradle.kts index 91646f312..cc4e40dd6 100644 --- a/firebase-database/build.gradle.kts +++ b/firebase-database/build.gradle.kts @@ -106,14 +106,14 @@ kotlin { nodejs { testTask { useMocha { - timeout = "5s" + timeout = "15s" } } } browser { testTask { useMocha { - timeout = "5s" + timeout = "15s" } } } diff --git a/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt index 062052aca..663f3becd 100644 --- a/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt @@ -12,7 +12,7 @@ expect fun runTest(test: suspend () -> Unit) class FirebaseDatabaseTest { @Serializable - data class DatabaseTest(val prop: String, val likes: Int = 0) + data class DatabaseTest(val title: String, val likes: Int = 0) @BeforeTest fun initializeFirebase() { @@ -34,51 +34,56 @@ class FirebaseDatabaseTest { } } - @AfterTest - fun tearDown() { - Firebase - .takeIf { Firebase.apps(context).isNotEmpty() } - ?.apply { app.delete() } - } - @Test fun testBasicIncrementTransaction() = runTest { - val data = DatabaseTest("post1", 2) + val data = DatabaseTest("PostOne", 2) val userRef = Firebase.database.reference("users/user_1/post_id_1") setupDatabase(userRef, data, DatabaseTest.serializer()) // Check database before transaction val userDocBefore = userRef.valueEvents.first().value(DatabaseTest.serializer()) - assertEquals(data.prop, userDocBefore.prop) + assertEquals(data.title, userDocBefore.title) assertEquals(data.likes, userDocBefore.likes) // Run transaction - userRef.runTransaction(DatabaseTest.serializer()) { DatabaseTest(data.prop, it.likes + 1) } + val transactionSnapshot = userRef.runTransaction(DatabaseTest.serializer()) { DatabaseTest(data.title, it.likes + 1) } + val userDocAfter = transactionSnapshot.value(DatabaseTest.serializer()) // Check the database after transaction - val userDocAfter = userRef.valueEvents.first().value(DatabaseTest.serializer()) - assertEquals(data.prop, userDocAfter.prop) - assertEquals(data.likes, userDocAfter.likes + 1) + assertEquals(data.title, userDocAfter.title) + assertEquals(data.likes + 1, userDocAfter.likes) + + // cleanUp Firebase + cleanUp() } @Test fun testBasicDecrementTransaction() = runTest { - val data = DatabaseTest("post2", 2) + val data = DatabaseTest("PostTwo", 2) val userRef = Firebase.database.reference("users/user_1/post_id_2") setupDatabase(userRef, data, DatabaseTest.serializer()) // Check database before transaction val userDocBefore = userRef.valueEvents.first().value(DatabaseTest.serializer()) - assertEquals(data.prop, userDocBefore.prop) + assertEquals(data.title, userDocBefore.title) assertEquals(data.likes, userDocBefore.likes) // Run transaction - userRef.runTransaction(DatabaseTest.serializer()) { DatabaseTest(data.prop, it.likes - 1) } + val transactionSnapshot = userRef.runTransaction(DatabaseTest.serializer()) { DatabaseTest(data.title, it.likes - 1) } + val userDocAfter = transactionSnapshot.value(DatabaseTest.serializer()) // Check the database after transaction - val userDocAfter = userRef.valueEvents.first().value(DatabaseTest.serializer()) - assertEquals(data.prop, userDocAfter.prop) - assertEquals(data.likes, userDocAfter.likes - 1) + assertEquals(data.title, userDocAfter.title) + assertEquals(data.likes - 1, userDocAfter.likes) + + // cleanUp Firebase + cleanUp() + } + + private fun cleanUp() { + Firebase + .takeIf { Firebase.apps(context).isNotEmpty() } + ?.apply { app.delete() } } private suspend fun setupDatabase(ref: DatabaseReference, data: T, strategy: SerializationStrategy) { From 2f7a650bf574a77f71939fcc6fb85a8ed9110a4c Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Tue, 24 Aug 2021 12:41:48 +0100 Subject: [PATCH 17/27] Remove await from js.transaction --- .../kotlin/dev/gitlive/firebase/database/database.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt index 9b531d0d4..919a216f3 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -141,7 +141,7 @@ actual class DatabaseReference internal constructor(override val js: firebase.da } }, applyLocally = false - ).await() + ) return deferred.await() } @@ -200,10 +200,13 @@ inline fun rethrow(function: () -> R): R { suspend fun Promise.awaitWhileOnline(): T = coroutineScope { +// this@awaitWhileOnline.await() val notConnected = Firebase.database .reference(".info/connected") .valueEvents - .filter { !it.value() } + .filter { + !it.value() + } .produceIn(this) select { From eccce95edeb5ba73bd59ac5812e0c7d5af799286 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Tue, 24 Aug 2021 12:45:14 +0100 Subject: [PATCH 18/27] Remove FIREBASE_DATABASE_EMULATOR_HOST env var --- .github/workflows/pull_request.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 914a042cc..5a9d85387 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -31,8 +31,6 @@ jobs: run: ./gradlew assemble - name: Run JS Tests run: ./gradlew cleanTest jsTest - env: - FIREBASE_DATABASE_EMULATOR_HOST: localhost:9000 - name: Upload JS test artifact uses: actions/upload-artifact@v2 if: failure() From 06634d2143658f920696fa763eb2a5d632d588d9 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Tue, 24 Aug 2021 12:47:35 +0100 Subject: [PATCH 19/27] Reset mocha timeouts to 5s --- firebase-database/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-database/build.gradle.kts b/firebase-database/build.gradle.kts index cc4e40dd6..91646f312 100644 --- a/firebase-database/build.gradle.kts +++ b/firebase-database/build.gradle.kts @@ -106,14 +106,14 @@ kotlin { nodejs { testTask { useMocha { - timeout = "15s" + timeout = "5s" } } } browser { testTask { useMocha { - timeout = "15s" + timeout = "5s" } } } From b97957d1efe9220753090fb425c4f1ae36bc8ba5 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Tue, 24 Aug 2021 13:58:49 +0100 Subject: [PATCH 20/27] Remove comment --- .github/workflows/pull_request.yml | 2 +- .../src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5a9d85387..107872fe7 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -58,4 +58,4 @@ jobs: if: failure() with: name: "Android Test Report HTML" - path: "firebase-firestore/build/reports/tests/androidTests/" \ No newline at end of file + path: "firebase-firestore/build/reports/tests/androidTests/" diff --git a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt index 919a216f3..bb1dd4b46 100644 --- a/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -200,7 +200,6 @@ inline fun rethrow(function: () -> R): R { suspend fun Promise.awaitWhileOnline(): T = coroutineScope { -// this@awaitWhileOnline.await() val notConnected = Firebase.database .reference(".info/connected") .valueEvents From 6e41e7015a38f199464aa9d32842382e85a89d76 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Thu, 17 Mar 2022 11:34:59 +0000 Subject: [PATCH 21/27] updates to database-transactions --- build.gradle.kts | 4 ++-- firebase-database/build.gradle.kts | 6 +++--- .../kotlin/dev/gitlive/firebase/database/database.kt | 10 +++++++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4c5d38a08..1dd939ccd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent plugins { - kotlin("multiplatform") version "1.5.31" apply false + kotlin("multiplatform") version "1.5.32" apply false id("base") } @@ -17,7 +17,7 @@ buildscript { } } dependencies { - classpath("com.android.tools.build:gradle:7.0.3") + classpath("com.android.tools.build:gradle:7.0.4") classpath("com.adarshr:gradle-test-logger-plugin:2.1.1") } } diff --git a/firebase-database/build.gradle.kts b/firebase-database/build.gradle.kts index 51cc9f243..b1d35659a 100644 --- a/firebase-database/build.gradle.kts +++ b/firebase-database/build.gradle.kts @@ -129,9 +129,9 @@ kotlin { apiVersion = "1.5" languageVersion = "1.5" progressiveMode = true - optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") - optIn("kotlinx.coroutines.FlowPreview") - optIn("kotlinx.serialization.InternalSerializationApi") +// optIn("kotlinx.coroutines.ExperimentalCoroutinesApi") +// optIn("kotlinx.coroutines.FlowPreview") +// optIn("kotlinx.serialization.InternalSerializationApi") } } diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt index 06f4e8ad0..dcd607ff7 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -192,15 +192,19 @@ actual class DatabaseReference internal constructor( val deferred = CompletableDeferred() android.runTransaction(object : Transaction.Handler { - override fun doTransaction(currentData: MutableData) = - Transaction.success(transactionUpdate(decode(strategy, currentData)) as MutableData) + override fun doTransaction(currentData: MutableData): Transaction.Result { + currentData.value = currentData.value?.let { + transactionUpdate(decode(strategy, it)) + } + return Transaction.success(currentData) + } override fun onComplete( error: DatabaseError?, committed: Boolean, snapshot: com.google.firebase.database.DataSnapshot? ) { - if(error != null) { + if (error != null) { deferred.completeExceptionally(error.toException()) } else { deferred.complete(DataSnapshot(snapshot!!)) From 87bb5434bc677f72cd3eefda3be655785fe5e181 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Fri, 18 Mar 2022 16:53:55 +0000 Subject: [PATCH 22/27] update serialization to 1.5.32 --- firebase-common/build.gradle.kts | 2 +- firebase-database/build.gradle.kts | 2 +- firebase-firestore/build.gradle.kts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firebase-common/build.gradle.kts b/firebase-common/build.gradle.kts index 44f7521e5..2794d4ac1 100644 --- a/firebase-common/build.gradle.kts +++ b/firebase-common/build.gradle.kts @@ -9,7 +9,7 @@ version = project.property("firebase-common.version") as String plugins { id("com.android.library") kotlin("multiplatform") - kotlin("plugin.serialization") version "1.5.31" + kotlin("plugin.serialization") version "1.5.32" } android { diff --git a/firebase-database/build.gradle.kts b/firebase-database/build.gradle.kts index b1d35659a..b320287e1 100644 --- a/firebase-database/build.gradle.kts +++ b/firebase-database/build.gradle.kts @@ -10,7 +10,7 @@ version = project.property("firebase-database.version") as String plugins { id("com.android.library") kotlin("multiplatform") - kotlin("plugin.serialization") version "1.5.10" + kotlin("plugin.serialization") version "1.5.32" } repositories { diff --git a/firebase-firestore/build.gradle.kts b/firebase-firestore/build.gradle.kts index dbd1d798f..7a5823ca9 100644 --- a/firebase-firestore/build.gradle.kts +++ b/firebase-firestore/build.gradle.kts @@ -10,7 +10,7 @@ version = project.property("firebase-firestore.version") as String plugins { id("com.android.library") kotlin("multiplatform") - kotlin("plugin.serialization") version "1.5.31" + kotlin("plugin.serialization") version "1.5.32" } android { From 9ae03aec36d38bebbfafe808de34c42ecbcf7b5d Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Fri, 18 Mar 2022 18:08:31 +0000 Subject: [PATCH 23/27] use .value --- .../iosMain/kotlin/dev/gitlive/firebase/database/database.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt index c7c68745c..0a3b72085 100644 --- a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -162,7 +162,7 @@ actual class DatabaseReference internal constructor( val deferred = CompletableDeferred() ios.runTransactionBlock( block = { firMutableData -> - FIRTransactionResult.successWithValue(transactionUpdate(decode(strategy, firMutableData)) as FIRMutableData) + FIRTransactionResult.successWithValue(transactionUpdate(decode(strategy, firMutableData.value))) }, andCompletionBlock = { error, _, snapshot -> if (error != null) { From b928409fea6184507ad2dd6efb342789ffe3ab78 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Wed, 27 Jul 2022 13:45:02 +0100 Subject: [PATCH 24/27] enable the new memory model and disable freezing --- gradle.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gradle.properties b/gradle.properties index dfd9c82b1..73f691ff8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,8 @@ kotlin.native.cacheKind.iosX64=none #kotlin.native.enableDependencyPropagation=false kotlin.native.enableParallelExecutionCheck=false kotlin.setJvmTargetFromAndroidCompileOptions=true +kotlin.native.binary.memoryModel=experimental +kotlin.native.binary.freezing=disabled org.gradle.jvmargs=-Xmx2048m org.gradle.parallel=true systemProp.org.gradle.internal.publish.checksums.insecure=true From 3c95915cb19bc2148e403c546c037274f1670198 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Thu, 28 Jul 2022 09:27:43 +0100 Subject: [PATCH 25/27] move database tests into single file --- .../dev.gitlive.firebase.database/database.kt | 80 ------------------- .../dev/gitlive/firebase/database/database.kt | 43 ++++++++++ 2 files changed, 43 insertions(+), 80 deletions(-) delete mode 100644 firebase-database/src/commonTest/kotlin/dev.gitlive.firebase.database/database.kt diff --git a/firebase-database/src/commonTest/kotlin/dev.gitlive.firebase.database/database.kt b/firebase-database/src/commonTest/kotlin/dev.gitlive.firebase.database/database.kt deleted file mode 100644 index 265f7b5f4..000000000 --- a/firebase-database/src/commonTest/kotlin/dev.gitlive.firebase.database/database.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2020 GitLive Ltd. Use of this source code is governed by the Apache 2.0 license. - */ - -package dev.gitlive.firebase.database - -import dev.gitlive.firebase.* -import kotlinx.coroutines.flow.first -import kotlinx.serialization.* -import kotlin.test.* - -expect val emulatorHost: String -expect val context: Any -expect fun runTest(test: suspend () -> Unit) - -class FirebaseDatabaseTest { - - @Serializable - data class FirebaseDatabaseChildTest(val prop1: String? = null, val time: Double = 0.0) - - @BeforeTest - fun initializeFirebase() { - Firebase - .takeIf { Firebase.apps(context).isEmpty() } - ?.apply { - initialize( - context, - FirebaseOptions( - applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a", - apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0", - databaseUrl = "https://fir-kotlin-sdk.firebaseio.com", - storageBucket = "fir-kotlin-sdk.appspot.com", - projectId = "fir-kotlin-sdk", - gcmSenderId = "846484016111" - ) - ) - Firebase.database.useEmulator(emulatorHost, 9000) - } - } - - @Test - fun testSetValue() = runTest { - val testValue = "test" - val testReference = Firebase.database.reference("testPath") - - testReference.setValue(testValue) - - val testReferenceValue = testReference - .valueEvents - .first() - .value() - - assertEquals(testValue, testReferenceValue) - } - - @Test - fun testChildCount() = runTest { - setupRealtimeData() - val dataSnapshot = Firebase.database - .reference("FirebaseRealtimeDatabaseTest") - .valueEvents - .first() - - val firebaseDatabaseChildCount = dataSnapshot.children.count() - assertEquals(3, firebaseDatabaseChildCount) - } - - private suspend fun setupRealtimeData() { - val firebaseDatabaseTestReference = Firebase.database - .reference("FirebaseRealtimeDatabaseTest") - - val firebaseDatabaseChildTest1 = FirebaseDatabaseChildTest("aaa") - val firebaseDatabaseChildTest2 = FirebaseDatabaseChildTest("bbb") - val firebaseDatabaseChildTest3 = FirebaseDatabaseChildTest("ccc") - - firebaseDatabaseTestReference.child("1").setValue(firebaseDatabaseChildTest1) - firebaseDatabaseTestReference.child("2").setValue(firebaseDatabaseChildTest2) - firebaseDatabaseTestReference.child("3").setValue(firebaseDatabaseChildTest3) - } -} \ No newline at end of file diff --git a/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt index 663f3becd..6e3d62bec 100644 --- a/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt @@ -11,6 +11,9 @@ expect fun runTest(test: suspend () -> Unit) class FirebaseDatabaseTest { + @Serializable + data class FirebaseDatabaseChildTest(val prop1: String? = null, val time: Double = 0.0) + @Serializable data class DatabaseTest(val title: String, val likes: Int = 0) @@ -34,6 +37,33 @@ class FirebaseDatabaseTest { } } + @Test + fun testSetValue() = runTest { + val testValue = "test" + val testReference = Firebase.database.reference("testPath") + + testReference.setValue(testValue) + + val testReferenceValue = testReference + .valueEvents + .first() + .value() + + assertEquals(testValue, testReferenceValue) + } + + @Test + fun testChildCount() = runTest { + setupRealtimeData() + val dataSnapshot = Firebase.database + .reference("FirebaseRealtimeDatabaseTest") + .valueEvents + .first() + + val firebaseDatabaseChildCount = dataSnapshot.children.count() + assertEquals(3, firebaseDatabaseChildCount) + } + @Test fun testBasicIncrementTransaction() = runTest { val data = DatabaseTest("PostOne", 2) @@ -80,6 +110,19 @@ class FirebaseDatabaseTest { cleanUp() } + private suspend fun setupRealtimeData() { + val firebaseDatabaseTestReference = Firebase.database + .reference("FirebaseRealtimeDatabaseTest") + + val firebaseDatabaseChildTest1 = FirebaseDatabaseChildTest("aaa") + val firebaseDatabaseChildTest2 = FirebaseDatabaseChildTest("bbb") + val firebaseDatabaseChildTest3 = FirebaseDatabaseChildTest("ccc") + + firebaseDatabaseTestReference.child("1").setValue(firebaseDatabaseChildTest1) + firebaseDatabaseTestReference.child("2").setValue(firebaseDatabaseChildTest2) + firebaseDatabaseTestReference.child("3").setValue(firebaseDatabaseChildTest3) + } + private fun cleanUp() { Firebase .takeIf { Firebase.apps(context).isNotEmpty() } From cbcdd484e9757145daa23ab64f7ebc6efe094998 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Mon, 1 Aug 2022 23:07:21 +0100 Subject: [PATCH 26/27] import CompletableDeferred --- .../androidMain/kotlin/dev/gitlive/firebase/database/database.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt index 978629e6c..7e51e4fdd 100644 --- a/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -20,6 +20,7 @@ import kotlinx.coroutines.flow.produceIn import kotlinx.coroutines.selects.select import kotlinx.coroutines.tasks.asDeferred import kotlinx.coroutines.tasks.await +import kotlinx.coroutines.CompletableDeferred import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy From 2dff075305ce125501aabb5899b8453898659764 Mon Sep 17 00:00:00 2001 From: Michael Richardson Date: Mon, 1 Aug 2022 23:08:05 +0100 Subject: [PATCH 27/27] return correct type to FIRTransactionResult --- .../kotlin/dev/gitlive/firebase/database/database.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt index 26372b6bf..7427ff201 100644 --- a/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt +++ b/firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt @@ -13,7 +13,6 @@ import dev.gitlive.firebase.database.ChildEvent.Type.* import dev.gitlive.firebase.decode import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.channels.ClosedSendChannelException import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.callbackFlow @@ -164,7 +163,10 @@ actual class DatabaseReference internal constructor( val deferred = CompletableDeferred() ios.runTransactionBlock( block = { firMutableData -> - FIRTransactionResult.successWithValue(transactionUpdate(decode(strategy, firMutableData.value))) + firMutableData?.value = firMutableData?.value?.let { + transactionUpdate(decode(strategy, firMutableData.value)) + } + FIRTransactionResult.successWithValue(firMutableData!!) }, andCompletionBlock = { error, _, snapshot -> if (error != null) {