Skip to content

Commit

Permalink
Moved Firestore internals to different package
Browse files Browse the repository at this point in the history
  • Loading branch information
Daeda88 committed Apr 21, 2024
1 parent f615bc3 commit cce8b82
Show file tree
Hide file tree
Showing 43 changed files with 1,720 additions and 1,295 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dev.gitlive.firebase.firestore.internal

import dev.gitlive.firebase.firestore.NativeCollectionReference
import dev.gitlive.firebase.internal.EncodedObject
import dev.gitlive.firebase.internal.android
import kotlinx.coroutines.tasks.await

@PublishedApi
internal actual class NativeCollectionReferenceWrapper internal actual constructor(actual override val native: NativeCollectionReference) : NativeQueryWrapper(native) {

actual val path: String
get() = native.path

actual val document: NativeDocumentReference
get() = NativeDocumentReference(native.document())

actual val parent: NativeDocumentReference?
get() = native.parent?.let{ NativeDocumentReference(it) }

actual fun document(documentPath: String) =
NativeDocumentReference(native.document(documentPath))

actual suspend fun addEncoded(data: EncodedObject) =
NativeDocumentReference(native.add(data.android).await())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package dev.gitlive.firebase.firestore.internal

import com.google.android.gms.tasks.TaskExecutors
import com.google.firebase.firestore.MetadataChanges
import dev.gitlive.firebase.firestore.EncodedFieldPath
import dev.gitlive.firebase.firestore.NativeDocumentReferenceType
import dev.gitlive.firebase.firestore.NativeDocumentSnapshot
import dev.gitlive.firebase.firestore.Source
import dev.gitlive.firebase.firestore.performUpdate
import dev.gitlive.firebase.internal.EncodedObject
import dev.gitlive.firebase.internal.android
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.tasks.await

@PublishedApi
internal actual class NativeDocumentReference actual constructor(actual val nativeValue: NativeDocumentReferenceType) {
val android: NativeDocumentReferenceType by ::nativeValue
actual val id: String
get() = android.id

actual val path: String
get() = android.path

actual val parent: NativeCollectionReferenceWrapper
get() = NativeCollectionReferenceWrapper(android.parent)

actual fun collection(collectionPath: String) = android.collection(collectionPath)

actual suspend fun get(source: Source) =
android.get(source.toAndroidSource()).await()

actual suspend fun setEncoded(encodedData: EncodedObject, setOptions: SetOptions) {
val task = (setOptions.android?.let {
android.set(encodedData.android, it)
} ?: android.set(encodedData.android))
task.await()
}

actual suspend fun updateEncoded(encodedData: EncodedObject) {
android.update(encodedData.android).await()
}

actual suspend fun updateEncodedFieldsAndValues(encodedFieldsAndValues: List<Pair<String, Any?>>) {
encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() }?.let {
android.update(encodedFieldsAndValues.toMap())
}?.await()
}

actual suspend fun updateEncodedFieldPathsAndValues(encodedFieldsAndValues: List<Pair<EncodedFieldPath, Any?>>) {
encodedFieldsAndValues.takeUnless { encodedFieldsAndValues.isEmpty() }
?.performUpdate { field, value, moreFieldsAndValues ->
android.update(field, value, *moreFieldsAndValues)
}?.await()
}

actual suspend fun delete() {
android.delete().await()
}

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

actual fun snapshots(includeMetadataChanges: Boolean) = addSnapshotListener(includeMetadataChanges) { snapshot, exception ->
snapshot?.let { trySend(snapshot) }
exception?.let { close(exception) }
}

override fun equals(other: Any?): Boolean =
this === other || other is NativeDocumentReference && nativeValue == other.nativeValue
override fun hashCode(): Int = nativeValue.hashCode()
override fun toString(): String = nativeValue.toString()

private fun addSnapshotListener(
includeMetadataChanges: Boolean = false,
listener: ProducerScope<NativeDocumentSnapshot>.(com.google.firebase.firestore.DocumentSnapshot?, com.google.firebase.firestore.FirebaseFirestoreException?) -> Unit
) = callbackFlow {
val executor = callbackExecutorMap[android.firestore] ?: TaskExecutors.MAIN_THREAD
val metadataChanges =
if (includeMetadataChanges) MetadataChanges.INCLUDE else MetadataChanges.EXCLUDE
val registration =
android.addSnapshotListener(executor, metadataChanges) { snapshots, exception ->
listener(snapshots, exception)
}
awaitClose { registration.remove() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dev.gitlive.firebase.firestore.internal

import dev.gitlive.firebase.firestore.EncodedFieldPath
import dev.gitlive.firebase.firestore.ServerTimestampBehavior
import dev.gitlive.firebase.firestore.SnapshotMetadata

@PublishedApi
internal actual class NativeDocumentSnapshotWrapper actual internal constructor(actual val native: com.google.firebase.firestore.DocumentSnapshot) {

actual val id get() = native.id
actual val reference get() = NativeDocumentReference(native.reference)

actual fun getEncoded(field: String, serverTimestampBehavior: ServerTimestampBehavior): Any? = native.get(field, serverTimestampBehavior.toAndroid())
actual fun getEncoded(fieldPath: EncodedFieldPath, serverTimestampBehavior: ServerTimestampBehavior): Any? = native.get(fieldPath, serverTimestampBehavior.toAndroid())
actual fun encodedData(serverTimestampBehavior: ServerTimestampBehavior): Any? = native.getData(serverTimestampBehavior.toAndroid())

actual fun contains(field: String) = native.contains(field)
actual fun contains(fieldPath: EncodedFieldPath) = native.contains(fieldPath)

actual val exists get() = native.exists()

actual val metadata: SnapshotMetadata get() = SnapshotMetadata(native.metadata)

fun ServerTimestampBehavior.toAndroid(): com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior = when (this) {
ServerTimestampBehavior.ESTIMATE -> com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior.ESTIMATE
ServerTimestampBehavior.NONE -> com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior.NONE
ServerTimestampBehavior.PREVIOUS -> com.google.firebase.firestore.DocumentSnapshot.ServerTimestampBehavior.PREVIOUS
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package dev.gitlive.firebase.firestore.internal

import com.google.android.gms.tasks.TaskExecutors
import com.google.firebase.firestore.MemoryCacheSettings
import com.google.firebase.firestore.MemoryEagerGcSettings
import com.google.firebase.firestore.MemoryLruGcSettings
import com.google.firebase.firestore.PersistentCacheSettings
import com.google.firebase.firestore.firestoreSettings
import dev.gitlive.firebase.firestore.FirebaseFirestoreSettings
import dev.gitlive.firebase.firestore.LocalCacheSettings
import dev.gitlive.firebase.firestore.MemoryGarbageCollectorSettings
import dev.gitlive.firebase.firestore.NativeFirebaseFirestore
import dev.gitlive.firebase.firestore.NativeTransaction
import dev.gitlive.firebase.firestore.android
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.tasks.await

internal actual class NativeFirebaseFirestoreWrapper actual constructor(actual val native: NativeFirebaseFirestore) {

actual var settings: FirebaseFirestoreSettings
get() = with(native.firestoreSettings) {
FirebaseFirestoreSettings(
isSslEnabled,
host,
cacheSettings?.let { localCacheSettings ->
when (localCacheSettings) {
is MemoryCacheSettings -> {
val garbageCollectionSettings =
when (val settings = localCacheSettings.garbageCollectorSettings) {
is MemoryEagerGcSettings -> MemoryGarbageCollectorSettings.Eager
is MemoryLruGcSettings -> MemoryGarbageCollectorSettings.LRUGC(
settings.sizeBytes
)

else -> throw IllegalArgumentException("Existing settings does not have valid GarbageCollectionSettings")
}
LocalCacheSettings.Memory(garbageCollectionSettings)
}

is PersistentCacheSettings -> LocalCacheSettings.Persistent(
localCacheSettings.sizeBytes
)

else -> throw IllegalArgumentException("Existing settings is not of a valid type")
}
} ?: kotlin.run {
@Suppress("DEPRECATION")
when {
isPersistenceEnabled -> LocalCacheSettings.Persistent(cacheSizeBytes)
cacheSizeBytes == FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED -> LocalCacheSettings.Memory(
MemoryGarbageCollectorSettings.Eager
)

else -> LocalCacheSettings.Memory(
MemoryGarbageCollectorSettings.LRUGC(
cacheSizeBytes
)
)
}
},
callbackExecutorMap[native] ?: TaskExecutors.MAIN_THREAD
)
}
set(value) {
native.firestoreSettings = firestoreSettings {
isSslEnabled = value.sslEnabled
host = value.host
setLocalCacheSettings(value.cacheSettings.android)
}
callbackExecutorMap[native] = value.callbackExecutor
}

actual fun collection(collectionPath: String) = native.collection(collectionPath)

actual fun collectionGroup(collectionId: String) = native.collectionGroup(collectionId)

actual fun document(documentPath: String) =
NativeDocumentReference(native.document(documentPath))

actual fun batch() = native.batch()

actual fun setLoggingEnabled(loggingEnabled: Boolean) =
com.google.firebase.firestore.FirebaseFirestore.setLoggingEnabled(loggingEnabled)

actual suspend fun <T> runTransaction(func: suspend NativeTransaction.() -> T): T =
native.runTransaction { runBlocking { it.func() } }.await()

actual suspend fun clearPersistence() =
native.clearPersistence().await().run { }

actual fun useEmulator(host: String, port: Int) {
native.useEmulator(host, port)
}

actual suspend fun disableNetwork() =
native.disableNetwork().await().run { }

actual suspend fun enableNetwork() =
native.enableNetwork().await().run { }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package dev.gitlive.firebase.firestore.internal

import com.google.android.gms.tasks.TaskExecutors
import com.google.firebase.firestore.FieldPath
import com.google.firebase.firestore.MetadataChanges
import com.google.firebase.firestore.Query
import dev.gitlive.firebase.firestore.Direction
import dev.gitlive.firebase.firestore.EncodedFieldPath
import dev.gitlive.firebase.firestore.Filter
import dev.gitlive.firebase.firestore.NativeDocumentSnapshot
import dev.gitlive.firebase.firestore.QuerySnapshot
import dev.gitlive.firebase.firestore.Source
import dev.gitlive.firebase.firestore.WhereConstraint
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.tasks.await

@PublishedApi
internal actual open class NativeQueryWrapper actual internal constructor(actual open val native: Query) {

actual fun limit(limit: Number) = native.limit(limit.toLong())

actual val snapshots get() = callbackFlow<QuerySnapshot> {
val listener = native.addSnapshotListener { snapshot, exception ->
snapshot?.let { trySend(QuerySnapshot(snapshot)) }
exception?.let { close(exception) }
}
awaitClose { listener.remove() }
}

actual fun snapshots(includeMetadataChanges: Boolean) = callbackFlow<QuerySnapshot> {
val metadataChanges =
if (includeMetadataChanges) MetadataChanges.INCLUDE else MetadataChanges.EXCLUDE
val listener = native.addSnapshotListener(metadataChanges) { snapshot, exception ->
snapshot?.let { trySend(QuerySnapshot(snapshot)) }
exception?.let { close(exception) }
}
awaitClose { listener.remove() }
}

actual suspend fun get(source: Source): QuerySnapshot =
QuerySnapshot(native.get(source.toAndroidSource()).await())

actual fun where(filter: Filter) = native.where(filter.toAndroidFilter())

private fun Filter.toAndroidFilter(): com.google.firebase.firestore.Filter = when (this) {
is Filter.And -> com.google.firebase.firestore.Filter.and(*filters.map { it.toAndroidFilter() }
.toTypedArray())
is Filter.Or -> com.google.firebase.firestore.Filter.or(*filters.map { it.toAndroidFilter() }
.toTypedArray())
is Filter.Field -> {
when (constraint) {
is WhereConstraint.ForNullableObject -> {
val modifier: (String, Any?) -> com.google.firebase.firestore.Filter = when (constraint) {
is WhereConstraint.EqualTo -> com.google.firebase.firestore.Filter::equalTo
is WhereConstraint.NotEqualTo -> com.google.firebase.firestore.Filter::notEqualTo
}
modifier.invoke(field, constraint.safeValue)
}
is WhereConstraint.ForObject -> {
val modifier: (String, Any) -> com.google.firebase.firestore.Filter = when (constraint) {
is WhereConstraint.LessThan -> com.google.firebase.firestore.Filter::lessThan
is WhereConstraint.GreaterThan -> com.google.firebase.firestore.Filter::greaterThan
is WhereConstraint.LessThanOrEqualTo -> com.google.firebase.firestore.Filter::lessThanOrEqualTo
is WhereConstraint.GreaterThanOrEqualTo -> com.google.firebase.firestore.Filter::greaterThanOrEqualTo
is WhereConstraint.ArrayContains -> com.google.firebase.firestore.Filter::arrayContains
}
modifier.invoke(field, constraint.safeValue)
}
is WhereConstraint.ForArray -> {
val modifier: (String, List<Any>) -> com.google.firebase.firestore.Filter = when (constraint) {
is WhereConstraint.InArray -> com.google.firebase.firestore.Filter::inArray
is WhereConstraint.ArrayContainsAny -> com.google.firebase.firestore.Filter::arrayContainsAny
is WhereConstraint.NotInArray -> com.google.firebase.firestore.Filter::notInArray
}
modifier.invoke(field, constraint.safeValues)
}
}
}
is Filter.Path -> {
when (constraint) {
is WhereConstraint.ForNullableObject -> {
val modifier: (FieldPath, Any?) -> com.google.firebase.firestore.Filter = when (constraint) {
is WhereConstraint.EqualTo -> com.google.firebase.firestore.Filter::equalTo
is WhereConstraint.NotEqualTo -> com.google.firebase.firestore.Filter::notEqualTo
}
modifier.invoke(path.android, constraint.safeValue)
}
is WhereConstraint.ForObject -> {
val modifier: (FieldPath, Any) -> com.google.firebase.firestore.Filter = when (constraint) {
is WhereConstraint.LessThan -> com.google.firebase.firestore.Filter::lessThan
is WhereConstraint.GreaterThan -> com.google.firebase.firestore.Filter::greaterThan
is WhereConstraint.LessThanOrEqualTo -> com.google.firebase.firestore.Filter::lessThanOrEqualTo
is WhereConstraint.GreaterThanOrEqualTo -> com.google.firebase.firestore.Filter::greaterThanOrEqualTo
is WhereConstraint.ArrayContains -> com.google.firebase.firestore.Filter::arrayContains
}
modifier.invoke(path.android, constraint.safeValue)
}
is WhereConstraint.ForArray -> {
val modifier: (FieldPath, List<Any>) -> com.google.firebase.firestore.Filter = when (constraint) {
is WhereConstraint.InArray -> com.google.firebase.firestore.Filter::inArray
is WhereConstraint.ArrayContainsAny -> com.google.firebase.firestore.Filter::arrayContainsAny
is WhereConstraint.NotInArray -> com.google.firebase.firestore.Filter::notInArray
}
modifier.invoke(path.android, constraint.safeValues)
}
}
}
}

actual fun orderBy(field: String, direction: Direction) = native.orderBy(field, direction)
actual fun orderBy(field: EncodedFieldPath, direction: Direction) = native.orderBy(field, direction)

actual fun startAfter(document: NativeDocumentSnapshot) = native.startAfter(document)
actual fun startAfter(vararg fieldValues: Any) = native.startAfter(*fieldValues)
actual fun startAt(document: NativeDocumentSnapshot) = native.startAt(document)
actual fun startAt(vararg fieldValues: Any) = native.startAt(*fieldValues)

actual fun endBefore(document: NativeDocumentSnapshot) = native.endBefore(document)
actual fun endBefore(vararg fieldValues: Any) = native.endBefore(*fieldValues)
actual fun endAt(document: NativeDocumentSnapshot) = native.endAt(document)
actual fun endAt(vararg fieldValues: Any) = native.endAt(*fieldValues)

private fun addSnapshotListener(
includeMetadataChanges: Boolean = false,
listener: ProducerScope<QuerySnapshot>.(com.google.firebase.firestore.QuerySnapshot?, com.google.firebase.firestore.FirebaseFirestoreException?) -> Unit
) = callbackFlow {
val executor = callbackExecutorMap[native.firestore] ?: TaskExecutors.MAIN_THREAD
val metadataChanges =
if (includeMetadataChanges) MetadataChanges.INCLUDE else MetadataChanges.EXCLUDE
val registration =
native.addSnapshotListener(executor, metadataChanges) { snapshots, exception ->
listener(snapshots, exception)
}
awaitClose { registration.remove() }
}
}
Loading

0 comments on commit cce8b82

Please sign in to comment.