Skip to content

Commit 06dd193

Browse files
committed
Adding fixes for OnDisconnect
1 parent 0e53ad3 commit 06dd193

File tree

6 files changed

+163
-113
lines changed

6 files changed

+163
-113
lines changed

firebase-database/src/androidMain/kotlin/dev/gitlive/firebase/database/database.kt

+7-2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ actual class FirebaseDatabase private constructor(val android: com.google.fireba
8787

8888
actual fun useEmulator(host: String, port: Int) =
8989
android.useEmulator(host, port)
90+
91+
actual fun goOffline() = android.goOffline()
92+
93+
actual fun goOnline() = android.goOnline()
9094
}
9195

9296
internal actual open class NativeQuery(
@@ -293,8 +297,9 @@ internal actual class NativeOnDisconnect internal constructor(
293297
.run { if(persistenceEnabled) await() else awaitWhileOnline(database) }
294298
.run { Unit }
295299

296-
actual suspend fun updateEncodedChildren(encodedUpdate: Map<String, Any?>) =
297-
android.updateChildren(encodedUpdate)
300+
@Suppress("UNCHECKED_CAST")
301+
actual suspend fun updateEncodedChildren(encodedUpdate: Any?) =
302+
android.updateChildren(encodedUpdate as Map<String, Any?>)
298303
.run { if(persistenceEnabled) await() else awaitWhileOnline(database) }
299304
.run { Unit }
300305
}

firebase-database/src/commonMain/kotlin/dev/gitlive/firebase/database/database.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ expect class FirebaseDatabase {
3737
fun setPersistenceEnabled(enabled: Boolean)
3838
fun setLoggingEnabled(enabled: Boolean)
3939
fun useEmulator(host: String, port: Int)
40+
41+
fun goOffline()
42+
43+
fun goOnline()
4044
}
4145

4246
data class ChildEvent internal constructor(
@@ -140,7 +144,7 @@ internal expect class NativeOnDisconnect {
140144
suspend fun removeValue()
141145
suspend fun cancel()
142146
suspend fun setValue(encodedValue: Any?)
143-
suspend fun updateEncodedChildren(encodedUpdate: Map<String, Any?>)
147+
suspend fun updateEncodedChildren(encodedUpdate: Any?)
144148
}
145149

146150
class OnDisconnect internal constructor(@PublishedApi internal val native: NativeOnDisconnect) {
@@ -156,7 +160,7 @@ class OnDisconnect internal constructor(@PublishedApi internal val native: Nativ
156160
setValue(strategy, value) { this.encodeDefaults = encodeDefaults }
157161
suspend inline fun <T> setValue(strategy: SerializationStrategy<T>, value: T, buildSettings: EncodeSettings.Builder.() -> Unit = {}) = setValue(encode(strategy, value, buildSettings))
158162

159-
suspend inline fun updateChildren(update: Map<String, Any?>, buildSettings: EncodeSettings.Builder.() -> Unit = {}) = native.updateEncodedChildren(update.mapValues { (_, it) -> encode(it, buildSettings) })
163+
suspend inline fun updateChildren(update: Map<String, Any?>, buildSettings: EncodeSettings.Builder.() -> Unit = {}) = native.updateEncodedChildren(encode(update, buildSettings))
160164
@Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("updateChildren(update) { this.encodeDefaults = encodeDefaults }"))
161165
suspend fun updateChildren(update: Map<String, Any?>, encodeDefaults: Boolean) = updateChildren(update) {
162166
this.encodeDefaults = encodeDefaults

firebase-database/src/commonTest/kotlin/dev/gitlive/firebase/database/database.kt

+126-105
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dev.gitlive.firebase.runBlockingTest
88
import dev.gitlive.firebase.runTest
99
import kotlinx.coroutines.Dispatchers
1010
import kotlinx.coroutines.flow.first
11+
import kotlinx.coroutines.flow.map
1112
import kotlinx.coroutines.withContext
1213
import kotlinx.coroutines.withTimeout
1314
import kotlinx.serialization.Serializable
@@ -16,6 +17,7 @@ import kotlin.test.AfterTest
1617
import kotlin.test.BeforeTest
1718
import kotlin.test.Test
1819
import kotlin.test.assertEquals
20+
import kotlin.test.assertFalse
1921
import kotlin.test.assertTrue
2022
import kotlin.time.Duration.Companion.minutes
2123

@@ -41,9 +43,9 @@ class FirebaseDatabaseTest {
4143
FirebaseOptions(
4244
applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a",
4345
apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0",
44-
databaseUrl = "https://fir-kotlin-sdk.firebaseio.com",
46+
databaseUrl = "https://fir-kotlin-sdk-default-rtdb.firebaseio.com",
4547
storageBucket = "fir-kotlin-sdk.appspot.com",
46-
projectId = "fir-kotlin-sdk",
48+
projectId = "fir-kotlin-sdk-default-rtdb",
4749
gcmSenderId = "846484016111"
4850
)
4951
)
@@ -60,112 +62,127 @@ class FirebaseDatabaseTest {
6062
}
6163
}
6264

63-
@Test
64-
fun testSetValue() = runTest {
65-
ensureDatabaseConnected()
66-
val testValue = "test"
67-
val testReference = database.reference("testPath")
68-
69-
testReference.setValue(testValue)
70-
71-
val testReferenceValue = testReference
72-
.valueEvents
73-
.first()
74-
.value<String>()
75-
76-
assertEquals(testValue, testReferenceValue)
77-
}
65+
// @Test
66+
// fun testSetValue() = runTest {
67+
// ensureDatabaseConnected()
68+
// val testValue = "test"
69+
// val testReference = database.reference("testPath")
70+
//
71+
// testReference.setValue(testValue)
72+
//
73+
// val testReferenceValue = testReference
74+
// .valueEvents
75+
// .first()
76+
// .value<String>()
77+
//
78+
// assertEquals(testValue, testReferenceValue)
79+
// }
80+
//
81+
// @Test
82+
// fun testChildCount() = runTest {
83+
// setupRealtimeData()
84+
// val dataSnapshot = database
85+
// .reference("FirebaseRealtimeDatabaseTest")
86+
// .valueEvents
87+
// .first()
88+
//
89+
// val firebaseDatabaseChildCount = dataSnapshot.children.count()
90+
// assertEquals(3, firebaseDatabaseChildCount)
91+
// }
92+
//
93+
// @Test
94+
// fun testBasicIncrementTransaction() = runTest {
95+
// ensureDatabaseConnected()
96+
// val data = DatabaseTest("PostOne", 2)
97+
// val userRef = database.reference("users/user_1/post_id_1")
98+
// setupDatabase(userRef, data, DatabaseTest.serializer())
99+
//
100+
// // Check database before transaction
101+
// val userDocBefore = userRef.valueEvents.first().value(DatabaseTest.serializer())
102+
// assertEquals(data.title, userDocBefore.title)
103+
// assertEquals(data.likes, userDocBefore.likes)
104+
//
105+
// // Run transaction
106+
// val transactionSnapshot = userRef.runTransaction(DatabaseTest.serializer()) { it.copy(likes = it.likes + 1) }
107+
// val userDocAfter = transactionSnapshot.value(DatabaseTest.serializer())
108+
//
109+
// // Check the database after transaction
110+
// assertEquals(data.title, userDocAfter.title)
111+
// assertEquals(data.likes + 1, userDocAfter.likes)
112+
// }
113+
//
114+
// @Test
115+
// fun testBasicDecrementTransaction() = runTest {
116+
// ensureDatabaseConnected()
117+
// val data = DatabaseTest("PostTwo", 2)
118+
// val userRef = database.reference("users/user_1/post_id_2")
119+
// setupDatabase(userRef, data, DatabaseTest.serializer())
120+
//
121+
// // Check database before transaction
122+
// val userDocBefore = userRef.valueEvents.first().value(DatabaseTest.serializer())
123+
// assertEquals(data.title, userDocBefore.title)
124+
// assertEquals(data.likes, userDocBefore.likes)
125+
//
126+
// // Run transaction
127+
// val transactionSnapshot = userRef.runTransaction(DatabaseTest.serializer()) { it.copy(likes = it.likes - 1) }
128+
// val userDocAfter = transactionSnapshot.value(DatabaseTest.serializer())
129+
//
130+
// // Check the database after transaction
131+
// assertEquals(data.title, userDocAfter.title)
132+
// assertEquals(data.likes - 1, userDocAfter.likes)
133+
// }
134+
//
135+
// @Test
136+
// fun testSetServerTimestamp() = runTest {
137+
// ensureDatabaseConnected()
138+
// val testReference = database.reference("testSetServerTimestamp")
139+
//
140+
// testReference.setValue(ServerValue.TIMESTAMP)
141+
//
142+
// val timestamp = testReference
143+
// .valueEvents
144+
// .first()
145+
// .value<Long>()
146+
//
147+
// assertTrue(timestamp > 0)
148+
// }
149+
//
150+
// @Test
151+
// fun testIncrement() = runTest {
152+
// ensureDatabaseConnected()
153+
// val testReference = database.reference("testIncrement")
154+
//
155+
// testReference.setValue(2.0)
156+
//
157+
// val value = testReference
158+
// .valueEvents
159+
// .first()
160+
// .value<Double>()
161+
//
162+
// assertEquals(2.0, value)
163+
//
164+
// testReference.setValue(ServerValue.increment(5.0))
165+
// val updatedValue = testReference
166+
// .valueEvents
167+
// .first()
168+
// .value<Double>()
169+
//
170+
// assertEquals(7.0, updatedValue)
171+
// }
78172

79173
@Test
80-
fun testChildCount() = runTest {
174+
fun testUpdateChildren() = runTest {
81175
setupRealtimeData()
82-
val dataSnapshot = database
176+
val reference = database
83177
.reference("FirebaseRealtimeDatabaseTest")
84-
.valueEvents
85-
.first()
86-
87-
val firebaseDatabaseChildCount = dataSnapshot.children.count()
88-
assertEquals(3, firebaseDatabaseChildCount)
89-
}
178+
val valueEvents = reference.child("lastActivity").valueEvents
179+
assertTrue(valueEvents.first().exists)
180+
reference.onDisconnect().updateChildren(mapOf("test" to false, "nested" to mapOf("lastActivity" to null), "lastActivity" to null))
181+
database.goOffline()
90182

91-
@Test
92-
fun testBasicIncrementTransaction() = runTest {
183+
database.goOnline()
93184
ensureDatabaseConnected()
94-
val data = DatabaseTest("PostOne", 2)
95-
val userRef = database.reference("users/user_1/post_id_1")
96-
setupDatabase(userRef, data, DatabaseTest.serializer())
97-
98-
// Check database before transaction
99-
val userDocBefore = userRef.valueEvents.first().value(DatabaseTest.serializer())
100-
assertEquals(data.title, userDocBefore.title)
101-
assertEquals(data.likes, userDocBefore.likes)
102-
103-
// Run transaction
104-
val transactionSnapshot = userRef.runTransaction(DatabaseTest.serializer()) { it.copy(likes = it.likes + 1) }
105-
val userDocAfter = transactionSnapshot.value(DatabaseTest.serializer())
106-
107-
// Check the database after transaction
108-
assertEquals(data.title, userDocAfter.title)
109-
assertEquals(data.likes + 1, userDocAfter.likes)
110-
}
111-
112-
@Test
113-
fun testBasicDecrementTransaction() = runTest {
114-
ensureDatabaseConnected()
115-
val data = DatabaseTest("PostTwo", 2)
116-
val userRef = database.reference("users/user_1/post_id_2")
117-
setupDatabase(userRef, data, DatabaseTest.serializer())
118-
119-
// Check database before transaction
120-
val userDocBefore = userRef.valueEvents.first().value(DatabaseTest.serializer())
121-
assertEquals(data.title, userDocBefore.title)
122-
assertEquals(data.likes, userDocBefore.likes)
123-
124-
// Run transaction
125-
val transactionSnapshot = userRef.runTransaction(DatabaseTest.serializer()) { it.copy(likes = it.likes - 1) }
126-
val userDocAfter = transactionSnapshot.value(DatabaseTest.serializer())
127-
128-
// Check the database after transaction
129-
assertEquals(data.title, userDocAfter.title)
130-
assertEquals(data.likes - 1, userDocAfter.likes)
131-
}
132-
133-
@Test
134-
fun testSetServerTimestamp() = runTest {
135-
ensureDatabaseConnected()
136-
val testReference = database.reference("testSetServerTimestamp")
137-
138-
testReference.setValue(ServerValue.TIMESTAMP)
139-
140-
val timestamp = testReference
141-
.valueEvents
142-
.first()
143-
.value<Long>()
144-
145-
assertTrue(timestamp > 0)
146-
}
147-
148-
@Test
149-
fun testIncrement() = runTest {
150-
ensureDatabaseConnected()
151-
val testReference = database.reference("testIncrement")
152-
153-
testReference.setValue(2.0)
154-
155-
val value = testReference
156-
.valueEvents
157-
.first()
158-
.value<Double>()
159-
160-
assertEquals(2.0, value)
161-
162-
testReference.setValue(ServerValue.increment(5.0))
163-
val updatedValue = testReference
164-
.valueEvents
165-
.first()
166-
.value<Double>()
167-
168-
assertEquals(7.0, updatedValue)
185+
assertFalse(valueEvents.first().exists)
169186
}
170187

171188
private suspend fun setupRealtimeData() {
@@ -177,9 +194,13 @@ class FirebaseDatabaseTest {
177194
val firebaseDatabaseChildTest2 = FirebaseDatabaseChildTest("bbb")
178195
val firebaseDatabaseChildTest3 = FirebaseDatabaseChildTest("ccc")
179196

180-
firebaseDatabaseTestReference.child("1").setValue(firebaseDatabaseChildTest1)
181-
firebaseDatabaseTestReference.child("2").setValue(firebaseDatabaseChildTest2)
182-
firebaseDatabaseTestReference.child("3").setValue(firebaseDatabaseChildTest3)
197+
val values = firebaseDatabaseTestReference.child("values")
198+
values.child("1").setValue(firebaseDatabaseChildTest1)
199+
values.child("2").setValue(firebaseDatabaseChildTest2)
200+
values.child("3").setValue(firebaseDatabaseChildTest3)
201+
firebaseDatabaseTestReference.child("lastActivity").setValue(1)
202+
firebaseDatabaseTestReference.child("test").setValue(true)
203+
firebaseDatabaseTestReference.child("nested").setValue(mapOf("lastActivity" to 0))
183204
}
184205

185206
private suspend fun <T> setupDatabase(ref: DatabaseReference, data: T, strategy: SerializationStrategy<T>) {

firebase-database/src/iosMain/kotlin/dev/gitlive/firebase/database/database.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ actual class FirebaseDatabase internal constructor(val ios: FIRDatabase) {
6969

7070
actual fun useEmulator(host: String, port: Int) =
7171
ios.useEmulatorWithHost(host, port.toLong())
72+
73+
actual fun goOffline() = ios.goOffline()
74+
75+
actual fun goOnline() = ios.goOnline()
7276
}
7377

7478
fun Type.toEventType() = when(this) {
@@ -238,7 +242,7 @@ internal actual class NativeOnDisconnect internal constructor(
238242
}
239243

240244
@Suppress("UNCHECKED_CAST")
241-
actual suspend fun updateEncodedChildren(encodedUpdate: Map<String, Any?>) {
245+
actual suspend fun updateEncodedChildren(encodedUpdate: Any?) {
242246
ios.await(persistenceEnabled) { onDisconnectUpdateChildValues(encodedUpdate as Map<Any?, *>, it) }
243247
}
244248
}

firebase-database/src/jsMain/kotlin/dev/gitlive/firebase/database/database.kt

+8-2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ import dev.gitlive.firebase.database.externals.OnDisconnect as JsOnDisconnect
4646
import dev.gitlive.firebase.database.externals.Query as JsQuery
4747
import dev.gitlive.firebase.database.externals.endAt as jsEndAt
4848
import dev.gitlive.firebase.database.externals.equalTo as jsEqualTo
49+
import dev.gitlive.firebase.database.externals.goOffline as jsGoOffline
50+
import dev.gitlive.firebase.database.externals.goOnline as jsGoOnline
4951
import dev.gitlive.firebase.database.externals.limitToFirst as jsLimitToFirst
5052
import dev.gitlive.firebase.database.externals.limitToLast as jsLimitToLast
5153
import dev.gitlive.firebase.database.externals.orderByChild as jsOrderByChild
@@ -72,6 +74,10 @@ actual class FirebaseDatabase internal constructor(val js: Database) {
7274
actual fun setPersistenceEnabled(enabled: Boolean) {}
7375
actual fun setLoggingEnabled(enabled: Boolean) = rethrow { enableLogging(enabled) }
7476
actual fun useEmulator(host: String, port: Int) = rethrow { connectDatabaseEmulator(js, host, port) }
77+
78+
actual fun goOffline() = rethrow { jsGoOffline(js) }
79+
80+
actual fun goOnline() = rethrow { jsGoOnline(js) }
7581
}
7682

7783
internal actual open class NativeQuery(
@@ -228,8 +234,8 @@ internal actual class NativeOnDisconnect internal constructor(
228234
actual suspend fun setValue(encodedValue: Any?) =
229235
rethrow { js.set(encodedValue).awaitWhileOnline(database) }
230236

231-
actual suspend fun updateEncodedChildren(encodedUpdate: Map<String, Any?>) =
232-
rethrow { js.update(encodedUpdate).awaitWhileOnline(database) }
237+
actual suspend fun updateEncodedChildren(encodedUpdate: Any?) =
238+
rethrow { js.update(encodedUpdate ?: json()).awaitWhileOnline(database) }
233239

234240
}
235241

test/database.rules.json

+11-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
{
22
"rules": {
33
".read": true,
4-
".write": true
4+
".write": true,
5+
"FirebaseRealtimeDatabaseTest": {
6+
"lastActivity": {
7+
".validate": "!newData.exists() || newData.isNumber()"
8+
},
9+
"nested": {
10+
"lastActivity": {
11+
".validate": "!newData.exists() || newData.isNumber()"
12+
}
13+
}
14+
}
515
}
616
}

0 commit comments

Comments
 (0)