Skip to content

Commit ad577b3

Browse files
authored
Merge pull request #491 from Qw4z1/set-source
Adds an optional Source argument to DocumentReference and Query
2 parents 90e1bd6 + 8d43e47 commit ad577b3

File tree

6 files changed

+174
-32
lines changed

6 files changed

+174
-32
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package dev.gitlive.firebase.firestore
88
import com.google.firebase.firestore.MetadataChanges
99
import dev.gitlive.firebase.Firebase
1010
import dev.gitlive.firebase.FirebaseApp
11+
import dev.gitlive.firebase.firestore.Source.*
1112
import kotlinx.coroutines.channels.awaitClose
1213
import kotlinx.coroutines.flow.Flow
1314
import kotlinx.coroutines.flow.callbackFlow
@@ -171,8 +172,8 @@ internal actual class NativeDocumentReference actual constructor(actual val nati
171172

172173
actual fun collection(collectionPath: String) = NativeCollectionReference(android.collection(collectionPath))
173174

174-
actual suspend fun get() =
175-
NativeDocumentSnapshot(android.get().await())
175+
actual suspend fun get(source: Source) =
176+
NativeDocumentSnapshot(android.get(source.toAndroidSource()).await())
176177

177178
actual suspend fun setEncoded(encodedData: Any, setOptions: SetOptions) {
178179
val task = (setOptions.android?.let {
@@ -230,7 +231,7 @@ actual open class Query internal actual constructor(nativeQuery: NativeQuery) {
230231

231232
open val android = nativeQuery.android
232233

233-
actual suspend fun get() = QuerySnapshot(android.get().await())
234+
actual suspend fun get(source: Source) = QuerySnapshot(android.get(source.toAndroidSource()).await())
234235

235236
actual fun limit(limit: Number) = Query(NativeQuery(android.limit(limit.toLong())))
236237

@@ -428,3 +429,11 @@ actual class FieldPath private constructor(val android: com.google.firebase.fire
428429
}
429430

430431
actual typealias EncodedFieldPath = com.google.firebase.firestore.FieldPath
432+
433+
internal typealias NativeSource = com.google.firebase.firestore.Source
434+
435+
private fun Source.toAndroidSource() = when(this) {
436+
CACHE -> NativeSource.CACHE
437+
SERVER -> NativeSource.SERVER
438+
DEFAULT -> NativeSource.DEFAULT
439+
}

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ expect open class Query internal constructor(nativeQuery: NativeQuery) {
131131
fun limit(limit: Number): Query
132132
val snapshots: Flow<QuerySnapshot>
133133
fun snapshots(includeMetadataChanges: Boolean = false): Flow<QuerySnapshot>
134-
suspend fun get(): QuerySnapshot
134+
suspend fun get(source: Source = Source.DEFAULT): QuerySnapshot
135135

136136
internal fun where(filter: Filter): Query
137137

@@ -327,7 +327,7 @@ internal expect class NativeDocumentReference(nativeValue: NativeDocumentReferen
327327
fun snapshots(includeMetadataChanges: Boolean = false): Flow<NativeDocumentSnapshot>
328328

329329
fun collection(collectionPath: String): NativeCollectionReference
330-
suspend fun get(): NativeDocumentSnapshot
330+
suspend fun get(source: Source = Source.DEFAULT): NativeDocumentSnapshot
331331
suspend fun setEncoded(encodedData: Any, setOptions: SetOptions)
332332
suspend fun updateEncoded(encodedData: Any)
333333
suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List<Pair<String, Any?>>)
@@ -348,7 +348,7 @@ data class DocumentReference internal constructor(@PublishedApi internal val nat
348348
fun snapshots(includeMetadataChanges: Boolean = false): Flow<DocumentSnapshot> = native.snapshots(includeMetadataChanges).map(::DocumentSnapshot)
349349

350350
fun collection(collectionPath: String): CollectionReference = CollectionReference(native.collection(collectionPath))
351-
suspend fun get(): DocumentSnapshot = DocumentSnapshot(native.get())
351+
suspend fun get(source: Source = Source.DEFAULT): DocumentSnapshot = DocumentSnapshot(native.get(source))
352352

353353
@Deprecated("Deprecated. Use builder instead", replaceWith = ReplaceWith("set(data, merge) { this.encodeDefaults = encodeDefaults }"))
354354
suspend inline fun <reified T> set(data: T, encodeDefaults: Boolean, merge: Boolean = false) = set(data, merge) {
@@ -553,3 +553,9 @@ expect class FieldPath(vararg fieldNames: String) {
553553
}
554554

555555
expect class EncodedFieldPath
556+
557+
enum class Source {
558+
CACHE,
559+
SERVER,
560+
DEFAULT
561+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package dev.gitlive.firebase.firestore
2+
3+
import dev.gitlive.firebase.*
4+
import kotlin.test.*
5+
6+
/**
7+
* These tests are separated from other tests because
8+
* testing Firestore Source requires toggling persistence settings per test.
9+
*/
10+
class FirestoreSourceTest {
11+
lateinit var firestore: FirebaseFirestore
12+
13+
companion object {
14+
val testDoc = FirebaseFirestoreTest.FirestoreTest(
15+
"aaa",
16+
0.0,
17+
1,
18+
listOf("a", "aa", "aaa"),
19+
"notNull",
20+
)
21+
}
22+
23+
private suspend fun setDoc() {
24+
firestore.collection("testFirestoreQuerying").document("one").set(testDoc)
25+
}
26+
27+
private fun initializeFirebase(persistenceEnabled: Boolean = false) {
28+
val app = Firebase.apps(context).firstOrNull() ?: Firebase.initialize(
29+
context,
30+
FirebaseOptions(
31+
applicationId = "1:846484016111:ios:dd1f6688bad7af768c841a",
32+
apiKey = "AIzaSyCK87dcMFhzCz_kJVs2cT2AVlqOTLuyWV0",
33+
databaseUrl = "https://fir-kotlin-sdk.firebaseio.com",
34+
storageBucket = "fir-kotlin-sdk.appspot.com",
35+
projectId = "fir-kotlin-sdk",
36+
gcmSenderId = "846484016111"
37+
)
38+
)
39+
40+
firestore = Firebase.firestore(app).apply {
41+
useEmulator(emulatorHost, 8080)
42+
setSettings(persistenceEnabled = persistenceEnabled)
43+
}
44+
}
45+
46+
@AfterTest
47+
fun deinitializeFirebase() = runBlockingTest {
48+
Firebase.apps(context).forEach {
49+
it.delete()
50+
}
51+
}
52+
53+
@Test
54+
fun testGetFromServer_withPersistence() = runTest {
55+
initializeFirebase(persistenceEnabled = true)
56+
setDoc()
57+
val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.SERVER)
58+
assertTrue(doc.exists)
59+
assertFalse(doc.native.metadata.isFromCache)
60+
}
61+
62+
@Test
63+
fun testGetFromServer_withoutPersistence() = runTest {
64+
initializeFirebase(persistenceEnabled = false)
65+
setDoc()
66+
val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.SERVER)
67+
assertTrue(doc.exists)
68+
assertFalse(doc.native.metadata.isFromCache)
69+
}
70+
71+
@Test
72+
fun testGetFromCache() = runTest {
73+
initializeFirebase(persistenceEnabled = true)
74+
75+
// Warm up cache by setting a document
76+
setDoc()
77+
78+
val cachedDoc = firestore.collection("testFirestoreQuerying").document("one").get(Source.CACHE)
79+
assertTrue(cachedDoc.exists)
80+
assertTrue(cachedDoc.native.metadata.isFromCache)
81+
}
82+
83+
@Test
84+
fun testGetFromCache_withoutPersistence() = runTest {
85+
initializeFirebase(persistenceEnabled = false)
86+
setDoc()
87+
assertFailsWith(FirebaseFirestoreException::class) {
88+
firestore.collection("testFirestoreQuerying").document("one").get(Source.CACHE)
89+
}
90+
}
91+
92+
@Test
93+
fun testGetDefault_withPersistence() = runTest {
94+
initializeFirebase(persistenceEnabled = false)
95+
val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.DEFAULT)
96+
assertTrue(doc.exists)
97+
assertFalse(doc.native.metadata.isFromCache)
98+
}
99+
@Test
100+
fun testGet() = runTest {
101+
initializeFirebase(persistenceEnabled = false)
102+
val doc = firestore.collection("testFirestoreQuerying").document("one").get()
103+
assertTrue(doc.exists)
104+
assertFalse(doc.native.metadata.isFromCache)
105+
}
106+
107+
@Test
108+
fun testGetDefault_withoutPersistence() = runTest {
109+
initializeFirebase(persistenceEnabled = true)
110+
setDoc()
111+
val doc = firestore.collection("testFirestoreQuerying").document("one").get(Source.DEFAULT)
112+
assertTrue(doc.exists)
113+
// Firebase defaults to first fetching from server
114+
assertFalse(doc.native.metadata.isFromCache)
115+
}
116+
117+
}

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,8 @@ internal actual class NativeDocumentReference actual constructor(actual val nati
185185

186186
actual fun collection(collectionPath: String) = NativeCollectionReference(ios.collectionWithPath(collectionPath))
187187

188-
actual suspend fun get() =
189-
NativeDocumentSnapshot(awaitResult { ios.getDocumentWithCompletion(it) })
188+
actual suspend fun get(source: Source) =
189+
NativeDocumentSnapshot(awaitResult { ios.getDocumentWithSource(source.toIosSource(), it) })
190190

191191
actual suspend fun setEncoded(encodedData: Any, setOptions: SetOptions) = await {
192192
when (setOptions) {
@@ -235,7 +235,7 @@ actual open class Query internal actual constructor(nativeQuery: NativeQuery) {
235235

236236
open val ios: FIRQuery = nativeQuery.ios
237237

238-
actual suspend fun get() = QuerySnapshot(awaitResult { ios.getDocumentsWithCompletion(it) })
238+
actual suspend fun get(source: Source) = QuerySnapshot(awaitResult { ios.getDocumentsWithSource(source.toIosSource(),it) })
239239

240240
actual fun limit(limit: Number) = Query(ios.queryLimitedTo(limit.toLong()).native)
241241

@@ -485,3 +485,9 @@ suspend inline fun <T> await(function: (callback: (NSError?) -> Unit) -> T): T {
485485
job.await()
486486
return result
487487
}
488+
489+
private fun Source.toIosSource() = when (this) {
490+
Source.CACHE -> FIRFirestoreSource.FIRFirestoreSourceCache
491+
Source.SERVER -> FIRFirestoreSource.FIRFirestoreSourceServer
492+
Source.DEFAULT -> FIRFirestoreSource.FIRFirestoreSourceDefault
493+
}

firebase-firestore/src/jsMain/kotlin/dev/gitlive/firebase/firestore/externals/firestore.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,20 @@ external fun getDoc(
6868
options: Any? = definedExternally
6969
): Promise<DocumentSnapshot>
7070

71+
external fun getDocFromCache(
72+
reference: DocumentReference,
73+
): Promise<DocumentSnapshot>
74+
75+
external fun getDocFromServer(
76+
reference: DocumentReference,
77+
): Promise<DocumentSnapshot>
78+
7179
external fun getDocs(query: Query): Promise<QuerySnapshot>
7280

81+
external fun getDocsFromCache(query: Query): Promise<QuerySnapshot>
82+
83+
external fun getDocsFromServer(query: Query): Promise<QuerySnapshot>
84+
7385
external fun getFirestore(app: FirebaseApp? = definedExternally): Firestore
7486

7587
external fun increment(n: Int): FieldValue

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

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,8 @@ package dev.gitlive.firebase.firestore
77
import dev.gitlive.firebase.Firebase
88
import dev.gitlive.firebase.FirebaseApp
99
import dev.gitlive.firebase.FirebaseException
10-
import dev.gitlive.firebase.firestore.externals.Firestore
11-
import dev.gitlive.firebase.firestore.externals.QueryConstraint
12-
import dev.gitlive.firebase.firestore.externals.addDoc
13-
import dev.gitlive.firebase.firestore.externals.and
14-
import dev.gitlive.firebase.firestore.externals.clearIndexedDbPersistence
15-
import dev.gitlive.firebase.firestore.externals.connectFirestoreEmulator
16-
import dev.gitlive.firebase.firestore.externals.deleteDoc
17-
import dev.gitlive.firebase.firestore.externals.doc
10+
import dev.gitlive.firebase.firestore.externals.*
1811
import dev.gitlive.firebase.firestore.externals.documentId as jsDocumentId
19-
import dev.gitlive.firebase.firestore.externals.enableIndexedDbPersistence
20-
import dev.gitlive.firebase.firestore.externals.getDoc
21-
import dev.gitlive.firebase.firestore.externals.getDocs
22-
import dev.gitlive.firebase.firestore.externals.getFirestore
23-
import dev.gitlive.firebase.firestore.externals.initializeFirestore
24-
import dev.gitlive.firebase.firestore.externals.onSnapshot
25-
import dev.gitlive.firebase.firestore.externals.or
26-
import dev.gitlive.firebase.firestore.externals.orderBy
27-
import dev.gitlive.firebase.firestore.externals.query
28-
import dev.gitlive.firebase.firestore.externals.refEqual
29-
import dev.gitlive.firebase.firestore.externals.setDoc
30-
import dev.gitlive.firebase.firestore.externals.setLogLevel
31-
import dev.gitlive.firebase.firestore.externals.writeBatch
3212
import kotlinx.coroutines.GlobalScope
3313
import kotlinx.coroutines.await
3414
import kotlinx.coroutines.channels.awaitClose
@@ -218,7 +198,7 @@ internal actual class NativeDocumentReference actual constructor(actual val nati
218198

219199
actual fun collection(collectionPath: String) = rethrow { NativeCollectionReference(jsCollection(js, collectionPath)) }
220200

221-
actual suspend fun get() = rethrow { NativeDocumentSnapshot( getDoc(js).await()) }
201+
actual suspend fun get(source: Source) = rethrow { NativeDocumentSnapshot( js.get(source).await()) }
222202

223203
actual val snapshots: Flow<NativeDocumentSnapshot> get() = snapshots()
224204

@@ -276,7 +256,7 @@ actual open class Query internal actual constructor(nativeQuery: NativeQuery) {
276256

277257
open val js: JsQuery = nativeQuery.js
278258

279-
actual suspend fun get() = rethrow { QuerySnapshot(getDocs(js).await()) }
259+
actual suspend fun get(source: Source) = rethrow { QuerySnapshot(js.get(source).await()) }
280260

281261
actual fun limit(limit: Number) = Query(query(js, jsLimit(limit)))
282262

@@ -543,3 +523,15 @@ fun entriesOf(jsObject: dynamic): List<Pair<String, Any?>> =
543523
// from: https://discuss.kotlinlang.org/t/how-to-access-native-js-object-as-a-map-string-any/509/8
544524
fun mapOf(jsObject: dynamic): Map<String, Any?> =
545525
entriesOf(jsObject).toMap()
526+
527+
private fun NativeDocumentReferenceType.get(source: Source) = when (source) {
528+
Source.DEFAULT -> getDoc(this)
529+
Source.CACHE -> getDocFromCache(this)
530+
Source.SERVER -> getDocFromServer(this)
531+
}
532+
533+
private fun JsQuery.get(source: Source) = when (source) {
534+
Source.DEFAULT -> getDocs(this)
535+
Source.CACHE -> getDocsFromCache(this)
536+
Source.SERVER -> getDocsFromServer(this)
537+
}

0 commit comments

Comments
 (0)