diff --git a/backend/src/main/java/com/paligot/confily/backend/categories/CategoryDao.kt b/backend/src/main/java/com/paligot/confily/backend/categories/CategoryDao.kt index 5f4894bd9..294bf7779 100644 --- a/backend/src/main/java/com/paligot/confily/backend/categories/CategoryDao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/categories/CategoryDao.kt @@ -1,41 +1,53 @@ package com.paligot.confily.backend.categories -import com.paligot.confily.backend.internals.helpers.database.Database -import com.paligot.confily.backend.internals.helpers.database.get -import com.paligot.confily.backend.internals.helpers.database.getAll +import com.google.cloud.firestore.Firestore +import com.paligot.confily.backend.internals.helpers.database.batchDelete +import com.paligot.confily.backend.internals.helpers.database.diffRefs +import com.paligot.confily.backend.internals.helpers.database.getDocument +import com.paligot.confily.backend.internals.helpers.database.getDocuments +import com.paligot.confily.backend.internals.helpers.database.insert +import com.paligot.confily.backend.internals.helpers.database.update private const val CollectionName = "categories" -class CategoryDao(private val database: Database) { - suspend fun get(eventId: String, id: String): CategoryDb? = database.get( - eventId = eventId, - collectionName = CollectionName, - id = id - ) +class CategoryDao( + private val projectName: String, + private val firestore: Firestore +) { + fun get(eventId: String, id: String): CategoryDb? = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocument(id) - suspend fun getAll(eventId: String): List = database.getAll( - eventId = eventId, - collectionName = CollectionName - ) + fun getAll(eventId: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocuments() - suspend fun createOrUpdate(eventId: String, item: CategoryDb) { + fun createOrUpdate(eventId: String, item: CategoryDb) { if (item.id == null) { - database.insert( - eventId = eventId, - collectionName = CollectionName - ) { item.copy(id = it) } + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .insert { item.copy(id = it) } } else { - database.update( - eventId = eventId, - collectionName = CollectionName, - id = item.id, - item = item - ) + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .update(item.id, item) } } - suspend fun deleteDiff(eventId: String, ids: List) { - val diff = database.diff(eventId, CollectionName, ids) - database.deleteAll(eventId, CollectionName, diff) + fun deleteDiff(eventId: String, ids: List) { + val diff = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .diffRefs(ids) + firestore.batchDelete(diff) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/categories/CategoryModule.kt b/backend/src/main/java/com/paligot/confily/backend/categories/CategoryModule.kt index d43dbd1bc..3f06399d1 100644 --- a/backend/src/main/java/com/paligot/confily/backend/categories/CategoryModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/categories/CategoryModule.kt @@ -1,9 +1,10 @@ package com.paligot.confily.backend.categories import com.paligot.confily.backend.events.EventModule.eventDao -import com.paligot.confily.backend.internals.InternalModule.database +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore +import com.paligot.confily.backend.internals.SystemEnv.projectName object CategoryModule { - val categoryDao = lazy { CategoryDao(database.value) } + val categoryDao = lazy { CategoryDao(projectName, cloudFirestore.value) } val categoryRepository = lazy { CategoryRepository(eventDao.value, categoryDao.value) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/events/EventDao.kt b/backend/src/main/java/com/paligot/confily/backend/events/EventDao.kt index f2179b28d..b143a4a05 100644 --- a/backend/src/main/java/com/paligot/confily/backend/events/EventDao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/events/EventDao.kt @@ -1,83 +1,76 @@ package com.paligot.confily.backend.events +import com.google.cloud.firestore.Firestore import com.paligot.confily.backend.NotAuthorized import com.paligot.confily.backend.NotFoundException -import com.paligot.confily.backend.internals.helpers.database.BasicDatabase -import com.paligot.confily.backend.internals.helpers.database.get -import com.paligot.confily.backend.internals.helpers.database.getAll +import com.paligot.confily.backend.internals.helpers.database.getDocument +import com.paligot.confily.backend.internals.helpers.database.getDocuments +import com.paligot.confily.backend.internals.helpers.database.update +import com.paligot.confily.backend.internals.helpers.database.upsert class EventDao( private val projectName: String, - private val database: BasicDatabase + private val firestore: Firestore ) { - suspend fun list(): List = database - .getAll(projectName) + fun list(): List = firestore + .collection(projectName) + .getDocuments() .filter { it.published } - suspend fun get(id: String): EventDb? = database.get(projectName, id) + fun get(id: String): EventDb? = firestore + .collection(projectName) + .getDocument(id) - suspend fun getVerified(id: String, apiKey: String?): EventDb { - val eventDb = database.get(projectName, id) + fun getVerified(id: String, apiKey: String?): EventDb { + val eventDb = firestore + .collection(projectName) + .getDocument(id) ?: throw NotFoundException("Event $id Not Found") return if (eventDb.apiKey == apiKey) eventDb else throw NotAuthorized } - suspend fun createOrUpdate(event: EventDb) { - val existing = database.get(projectName, event.slugId) - if (existing == null) { - database.insert(projectName, event.slugId, event) - } else { - database.update( - projectName, - event.slugId, - event.copy(updatedAt = System.currentTimeMillis()) - ) - } + fun createOrUpdate(event: EventDb) { + firestore + .collection(projectName) + .upsert(event.slugId, event.copy(updatedAt = System.currentTimeMillis())) } - suspend fun updateMenus(eventId: String, apiKey: String, menus: List) { + fun updateMenus(eventId: String, apiKey: String, menus: List) { val existing = getVerified(eventId, apiKey) - database.update( - projectName, - eventId, - existing.copy(menus = menus, updatedAt = System.currentTimeMillis()) - ) + firestore + .collection(projectName) + .update(eventId, existing.copy(menus = menus, updatedAt = System.currentTimeMillis())) } - suspend fun updateCoc(eventId: String, apiKey: String, coc: String) { + fun updateCoc(eventId: String, apiKey: String, coc: String) { val existing = getVerified(eventId, apiKey) - database.update( - projectName, - eventId, - existing.copy(coc = coc, updatedAt = System.currentTimeMillis()) - ) + firestore + .collection(projectName) + .update(eventId, existing.copy(coc = coc, updatedAt = System.currentTimeMillis())) } - suspend fun updateFeatures(eventId: String, apiKey: String, hasNetworking: Boolean) { + fun updateFeatures(eventId: String, apiKey: String, hasNetworking: Boolean) { val existing = getVerified(eventId, apiKey) - database.update( - projectName, - eventId, - existing.copy( - features = FeaturesActivatedDb(hasNetworking = hasNetworking), - updatedAt = System.currentTimeMillis() + firestore + .collection(projectName) + .update( + eventId, + existing.copy( + features = FeaturesActivatedDb(hasNetworking = hasNetworking), + updatedAt = System.currentTimeMillis() + ) ) - ) } - suspend fun updateUpdatedAt(event: EventDb) { - database.update( - projectName, - event.slugId, - event.copy(updatedAt = System.currentTimeMillis()) - ) + fun updateUpdatedAt(event: EventDb) { + firestore + .collection(projectName) + .update(event.slugId, event.copy(updatedAt = System.currentTimeMillis())) } - suspend fun updateAgendaUpdatedAt(event: EventDb) { - database.update( - projectName, - event.slugId, - event.copy(agendaUpdatedAt = System.currentTimeMillis()) - ) + fun updateAgendaUpdatedAt(event: EventDb) { + firestore + .collection(projectName) + .update(event.slugId, event.copy(agendaUpdatedAt = System.currentTimeMillis())) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/events/EventModule.kt b/backend/src/main/java/com/paligot/confily/backend/events/EventModule.kt index bad1e8803..f4eaebc7e 100644 --- a/backend/src/main/java/com/paligot/confily/backend/events/EventModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/events/EventModule.kt @@ -2,7 +2,7 @@ package com.paligot.confily.backend.events import com.paligot.confily.backend.categories.CategoryModule.categoryDao import com.paligot.confily.backend.formats.FormatModule.formatDao -import com.paligot.confily.backend.internals.InternalModule.basicDatabase +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore import com.paligot.confily.backend.internals.SystemEnv.projectName import com.paligot.confily.backend.partners.PartnerModule.partnerDao import com.paligot.confily.backend.qanda.QAndAModule.qAndADao @@ -12,7 +12,7 @@ import com.paligot.confily.backend.speakers.SpeakerModule.speakerDao import com.paligot.confily.backend.third.parties.geocode.GeocodeModule.geocodeApi object EventModule { - val eventDao = lazy { EventDao(projectName, basicDatabase.value) } + val eventDao = lazy { EventDao(projectName, cloudFirestore.value) } val eventRepository = lazy { EventRepository( geocodeApi.value, diff --git a/backend/src/main/java/com/paligot/confily/backend/formats/FormatDao.kt b/backend/src/main/java/com/paligot/confily/backend/formats/FormatDao.kt index 820d5be5d..9c1e6ff77 100644 --- a/backend/src/main/java/com/paligot/confily/backend/formats/FormatDao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/formats/FormatDao.kt @@ -1,41 +1,53 @@ package com.paligot.confily.backend.formats -import com.paligot.confily.backend.internals.helpers.database.Database -import com.paligot.confily.backend.internals.helpers.database.get -import com.paligot.confily.backend.internals.helpers.database.getAll +import com.google.cloud.firestore.Firestore +import com.paligot.confily.backend.internals.helpers.database.batchDelete +import com.paligot.confily.backend.internals.helpers.database.diffRefs +import com.paligot.confily.backend.internals.helpers.database.getDocument +import com.paligot.confily.backend.internals.helpers.database.getDocuments +import com.paligot.confily.backend.internals.helpers.database.insert +import com.paligot.confily.backend.internals.helpers.database.update private const val CollectionName = "formats" -class FormatDao(private val database: Database) { - suspend fun get(eventId: String, id: String): FormatDb? = database.get( - eventId = eventId, - collectionName = CollectionName, - id = id - ) +class FormatDao( + private val projectName: String, + private val firestore: Firestore +) { + fun get(eventId: String, id: String): FormatDb? = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocument(id) - suspend fun getAll(eventId: String): List = database.getAll( - eventId = eventId, - collectionName = CollectionName - ) + fun getAll(eventId: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocuments() - suspend fun createOrUpdate(eventId: String, item: FormatDb) { + fun createOrUpdate(eventId: String, item: FormatDb) { if (item.id == null) { - database.insert( - eventId = eventId, - collectionName = CollectionName - ) { item.copy(id = it) } + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .insert { item.copy(id = it) } } else { - database.update( - eventId = eventId, - collectionName = CollectionName, - id = item.id, - item = item - ) + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .update(item.id, item) } } - suspend fun deleteDiff(eventId: String, ids: List) { - val diff = database.diff(eventId, CollectionName, ids) - database.deleteAll(eventId, CollectionName, diff) + fun deleteDiff(eventId: String, ids: List) { + val diff = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .diffRefs(ids) + firestore.batchDelete(diff) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/formats/FormatModule.kt b/backend/src/main/java/com/paligot/confily/backend/formats/FormatModule.kt index 53ed32a3d..b08ce43ac 100644 --- a/backend/src/main/java/com/paligot/confily/backend/formats/FormatModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/formats/FormatModule.kt @@ -1,9 +1,10 @@ package com.paligot.confily.backend.formats import com.paligot.confily.backend.events.EventModule.eventDao -import com.paligot.confily.backend.internals.InternalModule.database +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore +import com.paligot.confily.backend.internals.SystemEnv.projectName object FormatModule { - val formatDao = lazy { FormatDao(database.value) } + val formatDao = lazy { FormatDao(projectName, cloudFirestore.value) } val formatRepository = lazy { FormatRepository(eventDao.value, formatDao.value) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/InternalModule.kt b/backend/src/main/java/com/paligot/confily/backend/internals/InternalModule.kt index 8646b5876..fee7ecf16 100644 --- a/backend/src/main/java/com/paligot/confily/backend/internals/InternalModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/internals/InternalModule.kt @@ -1,26 +1,17 @@ package com.paligot.confily.backend.internals -import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore import com.paligot.confily.backend.internals.GoogleServicesModule.cloudStorage import com.paligot.confily.backend.internals.GoogleServicesModule.drive import com.paligot.confily.backend.internals.GoogleServicesModule.secretManager import com.paligot.confily.backend.internals.SystemEnv.gcpProjectId import com.paligot.confily.backend.internals.SystemEnv.isCloud import com.paligot.confily.backend.internals.SystemEnv.projectName -import com.paligot.confily.backend.internals.helpers.database.BasicDatabase -import com.paligot.confily.backend.internals.helpers.database.Database import com.paligot.confily.backend.internals.helpers.drive.DriveDataSource import com.paligot.confily.backend.internals.helpers.image.TranscoderImage import com.paligot.confily.backend.internals.helpers.secret.Secret import com.paligot.confily.backend.internals.helpers.storage.Storage object InternalModule { - val database: Lazy = lazy { - Database.Factory.create(cloudFirestore.value, projectName) - } - val basicDatabase: Lazy = lazy { - BasicDatabase.Factory.create(cloudFirestore.value) - } val driveDataSource: Lazy = lazy { DriveDataSource.Factory.create(drive.value) } diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/BasicDatabase.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/BasicDatabase.kt deleted file mode 100644 index 24c424a0e..000000000 --- a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/BasicDatabase.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.paligot.confily.backend.internals.helpers.database - -import com.google.cloud.firestore.Firestore -import kotlin.reflect.KClass - -interface BasicDatabase { - suspend fun count(collectionName: String): Long - suspend fun get(collectionName: String, id: String, clazz: KClass): T? - suspend fun getAll(collectionName: String, clazz: KClass): List - suspend fun query( - collectionName: String, - clazz: KClass, - vararg ops: WhereOperation - ): List> - - suspend fun insert(collectionName: String, eventId: String, item: T) - suspend fun update(collectionName: String, eventId: String, item: T) - - object Factory { - fun create(firestore: Firestore): BasicDatabase = FirestoreBasicDatabase(firestore) - } -} - -suspend inline fun BasicDatabase.getAll(collectionName: String): List = - getAll(collectionName, T::class) -suspend inline fun BasicDatabase.get(collectionName: String, id: String): T? = - get(collectionName, id, T::class) diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/CollectionReference.fetcher.ext.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/CollectionReference.fetcher.ext.kt new file mode 100644 index 000000000..ed5466700 --- /dev/null +++ b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/CollectionReference.fetcher.ext.kt @@ -0,0 +1,47 @@ +package com.paligot.confily.backend.internals.helpers.database + +import com.google.cloud.firestore.CollectionReference +import com.google.cloud.firestore.DocumentReference +import com.google.cloud.firestore.Query +import kotlin.reflect.KClass + +fun CollectionReference.docExists(childPath: String): Boolean = document(childPath).exists() + +fun CollectionReference.size(): Long = count().get().get().count + +fun CollectionReference.isEmpty(): Boolean = size() == 0L + +fun CollectionReference.isNotEmpty(): Boolean = !isEmpty() + +inline fun CollectionReference.getDocument(childPath: String): T? = + document(childPath).getDocument() + +inline fun CollectionReference.getDocuments(): List = + listDocuments().getDocuments() + +inline fun CollectionReference.map(transform: (T) -> R): List = + listDocuments().map { transform(it.getDocument()!!) } + +fun CollectionReference.diffRefs(ids: List): List = + listDocuments().filter { it.id in ids } + +inline fun CollectionReference.diff(ids: List): List = + listDocuments().filter { it.id in ids }.getDocuments() + +inline fun CollectionReference.query(vararg ops: WhereOperation): List = + query(T::class, *ops) + +fun CollectionReference.query(clazz: KClass, vararg ops: WhereOperation): List { + var query: Query? = null + ops.forEach { + val requester = if (query == null) this else query!! + query = when (it) { + is WhereOperation.WhereEquals<*> -> requester.whereEqualTo(it.left, it.right) + is WhereOperation.WhereNotEquals<*> -> requester.whereNotEqualTo(it.left, it.right) + is WhereOperation.WhereIn<*> -> requester.whereIn(it.left, it.right) + is WhereOperation.WhereNotIn<*> -> requester.whereNotIn(it.left, it.right) + } + } + if (query == null) error("You can't create a query without any where condition") + return query!!.get().get().documents.map { it.toObject(clazz.java) } +} diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/CollectionReference.operation.ext.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/CollectionReference.operation.ext.kt new file mode 100644 index 000000000..3f8487168 --- /dev/null +++ b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/CollectionReference.operation.ext.kt @@ -0,0 +1,30 @@ +package com.paligot.confily.backend.internals.helpers.database + +import com.google.cloud.firestore.CollectionReference +import com.google.cloud.firestore.SetOptions +import com.google.cloud.firestore.WriteResult +import kotlin.reflect.full.memberProperties + +fun CollectionReference.upsert(childPath: String, item: T): WriteResult { + return if (docExists(childPath)) { + update(childPath, item) + } else { + insert(childPath, item) + } +} + +fun CollectionReference.insert(childPath: String, item: T): WriteResult = + document(childPath).set(item).get() + +fun CollectionReference.insert(transform: (id: String) -> T): String { + val docRef = document() + docRef.set(transform(docRef.id)).get() + return docRef.id +} + +fun CollectionReference.update(childPath: String, item: T): WriteResult { + val map = item::class.memberProperties.associate { it.name to it.getter.call(item) } + return document(childPath).set(map, SetOptions.merge()).get() +} + +fun CollectionReference.delete(childPath: String): WriteResult = document(childPath).delete().get() diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/Database.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/Database.kt deleted file mode 100644 index 3d15eb723..000000000 --- a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/Database.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.paligot.confily.backend.internals.helpers.database - -import com.google.cloud.firestore.Firestore -import kotlin.reflect.KClass - -@Suppress("TooManyFunctions") -interface Database { - suspend fun count(eventId: String, collectionName: String): Long - suspend fun get(eventId: String, collectionName: String, id: String, clazz: KClass): T? - suspend fun getAll(eventId: String, collectionName: String, clazz: KClass): List - suspend fun query( - eventId: String, - collectionName: String, - clazz: KClass, - vararg ops: WhereOperation - ): List - - suspend fun insert(eventId: String, collectionName: String, id: String, item: T) - suspend fun insert(eventId: String, collectionName: String, transform: (id: String) -> T): String - suspend fun update(eventId: String, collectionName: String, id: String, item: T) - suspend fun delete(eventId: String, collectionName: String, id: String) - suspend fun delete(eventId: String, collectionName: String) - suspend fun deleteAll(eventId: String, collectionName: String, ids: List) - suspend fun diff(eventId: String, collectionName: String, ids: List): List - suspend fun diff(eventId: String, collectionName: String, ids: List, clazz: KClass): List - - object Factory { - fun create(firestore: Firestore, projectName: String): Database = FirestoreDatabase(firestore, projectName) - } -} - -infix fun String.whereEquals(that: R): WhereOperation.WhereEquals = - WhereOperation.WhereEquals(this, that) - -infix fun String.whereNotEquals(that: R): WhereOperation.WhereNotEquals = - WhereOperation.WhereNotEquals(this, that) - -infix fun String.whereIn(that: List): WhereOperation.WhereIn = - WhereOperation.WhereIn(this, that) - -infix fun String.whereNotIn(that: List): WhereOperation.WhereNotIn = - WhereOperation.WhereNotIn(this, that) - -sealed class WhereOperation(val left: String) { - class WhereEquals(left: String, val right: R) : WhereOperation(left) - class WhereNotEquals(left: String, val right: R) : WhereOperation(left) - class WhereIn(left: String, val right: List) : WhereOperation(left) - class WhereNotIn(left: String, val right: List) : WhereOperation(left) -} - -suspend inline fun Database.get(eventId: String, collectionName: String, id: String): T? = - get(eventId, collectionName, id, T::class) - -suspend inline fun Database.getAll(eventId: String, collectionName: String): List = - getAll(eventId, collectionName, T::class) - -suspend inline fun Database.query( - eventId: String, - collectionName: String, - vararg ops: WhereOperation -): List = query(eventId, collectionName, T::class, *ops) - -suspend inline fun Database.diff( - eventId: String, - collectionName: String, - ids: List -): List = diff(eventId, collectionName, ids, T::class) diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/DocumentReference.ext.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/DocumentReference.ext.kt new file mode 100644 index 000000000..1fea258e3 --- /dev/null +++ b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/DocumentReference.ext.kt @@ -0,0 +1,12 @@ +package com.paligot.confily.backend.internals.helpers.database + +import com.google.cloud.firestore.DocumentReference + +fun DocumentReference.exists(): Boolean = get().get().exists() + +inline fun DocumentReference.getDocument(): T? = getDocument(T::class.java) + +fun DocumentReference.getDocument(clazz: Class): T? = get().get().toObject(clazz) + +inline fun Iterable.getDocuments(): List = + map { it.getDocument()!! } diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/Firestore.ext.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/Firestore.ext.kt new file mode 100644 index 000000000..190145994 --- /dev/null +++ b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/Firestore.ext.kt @@ -0,0 +1,12 @@ +package com.paligot.confily.backend.internals.helpers.database + +import com.google.cloud.firestore.DocumentReference +import com.google.cloud.firestore.Firestore + +fun Firestore.batchDelete(docReferences: List) { + val batch = batch() + docReferences.forEach { + batch.delete(it) + } + batch.commit().get() +} diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/FirestoreBasicDatabase.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/FirestoreBasicDatabase.kt deleted file mode 100644 index be99e423a..000000000 --- a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/FirestoreBasicDatabase.kt +++ /dev/null @@ -1,78 +0,0 @@ -package com.paligot.confily.backend.internals.helpers.database - -import com.google.cloud.firestore.Firestore -import com.google.cloud.firestore.Query -import com.google.cloud.firestore.SetOptions -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import kotlin.reflect.KClass -import kotlin.reflect.full.memberProperties - -class FirestoreBasicDatabase(private val firestore: Firestore) : BasicDatabase { - override suspend fun count(collectionName: String): Long = withContext(Dispatchers.IO) { - firestore.collection(collectionName).count().get().get().count - } - - override suspend fun get(collectionName: String, id: String, clazz: KClass): T? = - withContext(Dispatchers.IO) { - firestore - .collection(collectionName) - .document(id) - .get() - .get() - .toObject(clazz.java) - } - - override suspend fun getAll(collectionName: String, clazz: KClass): List = - withContext(Dispatchers.IO) { - firestore - .collection(collectionName) - .listDocuments() - .map { it.get().get().toObject(clazz.java)!! } - } - - override suspend fun query( - collectionName: String, - clazz: KClass, - vararg ops: WhereOperation - ) = withContext(Dispatchers.IO) { - val collRef = firestore.collection(collectionName) - var query: Query? = null - ops.forEach { - val requester = if (query == null) collRef else query!! - query = when (it) { - is WhereOperation.WhereEquals<*> -> requester.whereEqualTo(it.left, it.right) - is WhereOperation.WhereNotEquals<*> -> requester.whereNotEqualTo(it.left, it.right) - is WhereOperation.WhereIn<*> -> requester.whereIn(it.left, it.right) - is WhereOperation.WhereNotIn<*> -> requester.whereNotIn(it.left, it.right) - } - } - if (query == null) error("You can't create a query without any where condition") - query!! - .get() - .get() - .documents - .map { it.id to it.toObject(clazz.java) } - } - - override suspend fun insert(collectionName: String, eventId: String, item: T) = - withContext(Dispatchers.IO) { - firestore - .collection(collectionName) - .document(eventId) - .set(item) - .get() - Unit - } - - override suspend fun update(collectionName: String, eventId: String, item: T) = - withContext(Dispatchers.IO) { - val map = item::class.memberProperties.associate { it.name to it.getter.call(item) } - firestore - .collection(collectionName) - .document(eventId) - .set(map, SetOptions.merge()) - .get() - Unit - } -} diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/FirestoreDatabase.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/FirestoreDatabase.kt deleted file mode 100644 index 232fcaec5..000000000 --- a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/FirestoreDatabase.kt +++ /dev/null @@ -1,203 +0,0 @@ -package com.paligot.confily.backend.internals.helpers.database - -import com.google.cloud.firestore.Firestore -import com.google.cloud.firestore.Query -import com.google.cloud.firestore.SetOptions -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.withContext -import kotlin.reflect.KClass -import kotlin.reflect.full.memberProperties - -@Suppress("TooManyFunctions") -class FirestoreDatabase(private val firestore: Firestore, private val projectName: String) : - Database { - override suspend fun count(eventId: String, collectionName: String): Long = - withContext(Dispatchers.IO) { - firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .count() - .get() - .get() - .count - } - - override suspend fun get( - eventId: String, - collectionName: String, - id: String, - clazz: KClass - ): T? = withContext(Dispatchers.IO) { - firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .document(id) - .get() - .get() - .toObject(clazz.java) - } - - override suspend fun getAll( - eventId: String, - collectionName: String, - clazz: KClass - ): List = withContext(Dispatchers.IO) { - firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .listDocuments() - .map { it.get().get().toObject(clazz.java)!! } - } - - override suspend fun query( - eventId: String, - collectionName: String, - clazz: KClass, - vararg ops: WhereOperation - ): List = withContext(Dispatchers.IO) { - val collRef = firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - var query: Query? = null - ops.forEach { - val requester = if (query == null) collRef else query!! - query = when (it) { - is WhereOperation.WhereEquals<*> -> requester.whereEqualTo(it.left, it.right) - is WhereOperation.WhereNotEquals<*> -> requester.whereNotEqualTo(it.left, it.right) - is WhereOperation.WhereIn<*> -> requester.whereIn(it.left, it.right) - is WhereOperation.WhereNotIn<*> -> requester.whereNotIn(it.left, it.right) - } - } - if (query == null) error("You can't create a query without any where condition") - query!! - .get() - .get() - .documents - .map { it.toObject(clazz.java) } - } - - override suspend fun insert( - eventId: String, - collectionName: String, - id: String, - item: T - ) = withContext(Dispatchers.IO) { - firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .document(id) - .set(item) - .get() - Unit - } - - override suspend fun insert( - eventId: String, - collectionName: String, - transform: (id: String) -> T - ): String = withContext(Dispatchers.IO) { - val docRef = firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .document() - val item = transform(docRef.id) - docRef.set(item).get() - return@withContext docRef.id - } - - override suspend fun update( - eventId: String, - collectionName: String, - id: String, - item: T - ) = withContext(Dispatchers.IO) { - val map = item::class.memberProperties.associate { it.name to it.getter.call(item) } - firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .document(id) - .set(map, SetOptions.merge()) - .get() - Unit - } - - override suspend fun delete(eventId: String, collectionName: String, id: String) = - withContext(Dispatchers.IO) { - firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .document(id) - .delete() - .get() - Unit - } - - override suspend fun delete(eventId: String, collectionName: String) = - withContext(Dispatchers.IO) { - val documents = firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .listDocuments() - documents.map { async { it.delete() } }.awaitAll() - Unit - } - - override suspend fun deleteAll( - eventId: String, - collectionName: String, - ids: List - ): Unit = withContext(Dispatchers.IO) { - val batch = firestore.batch() - ids.forEach { - batch.delete( - firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .document(it) - ) - } - batch.commit().get() - } - - override suspend fun diff( - eventId: String, - collectionName: String, - ids: List - ): List = withContext(Dispatchers.IO) { - val saved = firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .listDocuments() - .map { it.id } - saved - ids.toSet() - } - - override suspend fun diff( - eventId: String, - collectionName: String, - ids: List, - clazz: KClass - ): List = withContext(Dispatchers.IO) { - val saved = firestore - .collection(projectName) - .document(eventId) - .collection(collectionName) - .listDocuments() - saved - .filterNot { it.id in ids } - .map { it.get().get().toObject(clazz.java)!! } - } -} diff --git a/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/WhereOperation.kt b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/WhereOperation.kt new file mode 100644 index 000000000..02bb68120 --- /dev/null +++ b/backend/src/main/java/com/paligot/confily/backend/internals/helpers/database/WhereOperation.kt @@ -0,0 +1,20 @@ +package com.paligot.confily.backend.internals.helpers.database + +sealed class WhereOperation(val left: String) { + class WhereEquals(left: String, val right: R) : WhereOperation(left) + class WhereNotEquals(left: String, val right: R) : WhereOperation(left) + class WhereIn(left: String, val right: List) : WhereOperation(left) + class WhereNotIn(left: String, val right: List) : WhereOperation(left) +} + +infix fun String.whereEquals(that: R): WhereOperation.WhereEquals = + WhereOperation.WhereEquals(this, that) + +infix fun String.whereNotEquals(that: R): WhereOperation.WhereNotEquals = + WhereOperation.WhereNotEquals(this, that) + +infix fun String.whereIn(that: List): WhereOperation.WhereIn = + WhereOperation.WhereIn(this, that) + +infix fun String.whereNotIn(that: List): WhereOperation.WhereNotIn = + WhereOperation.WhereNotIn(this, that) diff --git a/backend/src/main/java/com/paligot/confily/backend/jobs/JobDao.kt b/backend/src/main/java/com/paligot/confily/backend/jobs/JobDao.kt index 7b6ef6cdc..2b4ac6ceb 100644 --- a/backend/src/main/java/com/paligot/confily/backend/jobs/JobDao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/jobs/JobDao.kt @@ -1,27 +1,40 @@ package com.paligot.confily.backend.jobs -import com.paligot.confily.backend.internals.helpers.database.Database -import com.paligot.confily.backend.internals.helpers.database.getAll +import com.google.cloud.firestore.Firestore +import com.paligot.confily.backend.internals.helpers.database.batchDelete +import com.paligot.confily.backend.internals.helpers.database.getDocuments +import com.paligot.confily.backend.internals.helpers.database.insert import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope private const val CollectionName = "jobs" -class JobDao(private val database: Database) { - suspend fun getAll(eventId: String): List = database - .getAll(eventId = eventId, collectionName = CollectionName) +class JobDao( + private val projectName: String, + private val firestore: Firestore +) { + fun getAll(eventId: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocuments() suspend fun resetJobs(eventId: String, jobs: List) = coroutineScope { - database.delete(eventId, CollectionName) + val docRefs = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .listDocuments() + .toList() + firestore.batchDelete(docRefs) val asyncItems = jobs.map { job -> async { - database.insert( - eventId = eventId, - collectionName = CollectionName, - id = job.id, - item = job - ) + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .insert(job.id, job) } } asyncItems.awaitAll() diff --git a/backend/src/main/java/com/paligot/confily/backend/jobs/JobModule.kt b/backend/src/main/java/com/paligot/confily/backend/jobs/JobModule.kt index cf3a63ca3..fc0ecb63e 100644 --- a/backend/src/main/java/com/paligot/confily/backend/jobs/JobModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/jobs/JobModule.kt @@ -1,12 +1,13 @@ package com.paligot.confily.backend.jobs import com.paligot.confily.backend.events.EventModule.eventDao -import com.paligot.confily.backend.internals.InternalModule.database +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore +import com.paligot.confily.backend.internals.SystemEnv.projectName import com.paligot.confily.backend.partners.PartnerModule.partnerDao import com.paligot.confily.backend.third.parties.welovedevs.WeLoveDevsModule.wldApi object JobModule { - val jobDao = lazy { JobDao(database.value) } + val jobDao = lazy { JobDao(projectName, cloudFirestore.value) } val jobRepository = lazy { JobRepository(wldApi.value, eventDao.value, partnerDao.value, jobDao.value) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/partners/PartnerDao.kt b/backend/src/main/java/com/paligot/confily/backend/partners/PartnerDao.kt index c22786326..31bac0b2b 100644 --- a/backend/src/main/java/com/paligot/confily/backend/partners/PartnerDao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/partners/PartnerDao.kt @@ -1,8 +1,10 @@ package com.paligot.confily.backend.partners -import com.paligot.confily.backend.internals.helpers.database.Database -import com.paligot.confily.backend.internals.helpers.database.get -import com.paligot.confily.backend.internals.helpers.database.getAll +import com.google.cloud.firestore.Firestore +import com.paligot.confily.backend.internals.helpers.database.insert +import com.paligot.confily.backend.internals.helpers.database.isNotEmpty +import com.paligot.confily.backend.internals.helpers.database.map +import com.paligot.confily.backend.internals.helpers.database.upsert import com.paligot.confily.backend.internals.helpers.image.Png import com.paligot.confily.backend.internals.helpers.storage.MimeType import com.paligot.confily.backend.internals.helpers.storage.Storage @@ -13,41 +15,41 @@ import kotlinx.coroutines.coroutineScope private const val CollectionName = "companies" -class PartnerDao(private val database: Database, private val storage: Storage) { - suspend fun getAll(eventId: String): List = database - .getAll(eventId = eventId, collectionName = CollectionName) - .map { +class PartnerDao( + private val projectName: String, + private val firestore: Firestore, + private val storage: Storage +) { + fun getAll(eventId: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .map { if (it.siteUrl.contains(Regex("^http[s]{0,1}://"))) return@map it return@map it.copy(siteUrl = "https://${it.siteUrl}") } - suspend fun createOrUpdate(eventId: String, partner: PartnerDb): String = coroutineScope { + fun createOrUpdate(eventId: String, partner: PartnerDb): String { if (partner.id == "") { - return@coroutineScope database.insert( - eventId = eventId, - collectionName = CollectionName - ) { partner.copy(id = it) } + return firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .insert { partner.copy(id = it) } } - val existing = database.get(eventId = eventId, collectionName = CollectionName, id = partner.id) - if (existing == null) { - database.insert( - eventId = eventId, - collectionName = CollectionName, - id = partner.id, - item = partner - ) - } else { - database.update( - eventId = eventId, - collectionName = CollectionName, - id = partner.id, - item = partner - ) - } - return@coroutineScope partner.id + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .upsert(partner.id, partner) + return partner.id } - suspend fun uploadPartnerLogos(eventId: String, partnerId: String, pngs: List): List = coroutineScope { + suspend fun uploadPartnerLogos( + eventId: String, + partnerId: String, + pngs: List + ): List = coroutineScope { return@coroutineScope pngs .filter { it.content != null } .map { png -> @@ -62,6 +64,9 @@ class PartnerDao(private val database: Database, private val storage: Storage) { .awaitAll() } - suspend fun hasPartners(eventId: String): Boolean = - database.count(eventId = eventId, collectionName = CollectionName) > 0 + fun hasPartners(eventId: String): Boolean = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .isNotEmpty() } diff --git a/backend/src/main/java/com/paligot/confily/backend/partners/PartnerModule.kt b/backend/src/main/java/com/paligot/confily/backend/partners/PartnerModule.kt index f83072e5f..2d9f9049c 100644 --- a/backend/src/main/java/com/paligot/confily/backend/partners/PartnerModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/partners/PartnerModule.kt @@ -1,14 +1,15 @@ package com.paligot.confily.backend.partners import com.paligot.confily.backend.events.EventModule.eventDao -import com.paligot.confily.backend.internals.InternalModule.database +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore import com.paligot.confily.backend.internals.InternalModule.storage import com.paligot.confily.backend.internals.InternalModule.transcoder +import com.paligot.confily.backend.internals.SystemEnv.projectName import com.paligot.confily.backend.jobs.JobModule.jobDao import com.paligot.confily.backend.third.parties.geocode.GeocodeModule.geocodeApi object PartnerModule { - val partnerDao = lazy { PartnerDao(database.value, storage.value) } + val partnerDao = lazy { PartnerDao(projectName, cloudFirestore.value, storage.value) } val partnerRepository = lazy { PartnerRepository( geocodeApi.value, diff --git a/backend/src/main/java/com/paligot/confily/backend/qanda/QAndADao.kt b/backend/src/main/java/com/paligot/confily/backend/qanda/QAndADao.kt index d71e62bd0..7bb3e986a 100644 --- a/backend/src/main/java/com/paligot/confily/backend/qanda/QAndADao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/qanda/QAndADao.kt @@ -1,46 +1,61 @@ package com.paligot.confily.backend.qanda -import com.paligot.confily.backend.internals.helpers.database.Database -import com.paligot.confily.backend.internals.helpers.database.get +import com.google.cloud.firestore.Firestore +import com.paligot.confily.backend.internals.helpers.database.batchDelete +import com.paligot.confily.backend.internals.helpers.database.diffRefs +import com.paligot.confily.backend.internals.helpers.database.getDocument +import com.paligot.confily.backend.internals.helpers.database.insert +import com.paligot.confily.backend.internals.helpers.database.isNotEmpty import com.paligot.confily.backend.internals.helpers.database.query +import com.paligot.confily.backend.internals.helpers.database.update import com.paligot.confily.backend.internals.helpers.database.whereEquals private const val CollectionName = "qanda" -class QAndADao(private val database: Database) { - suspend fun get(eventId: String, id: String): QAndADb? = database.get( - eventId = eventId, - collectionName = CollectionName, - id = id - ) +class QAndADao( + private val projectName: String, + private val firestore: Firestore +) { + fun get(eventId: String, id: String): QAndADb? = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocument(id) - suspend fun getAll(eventId: String, language: String): List = database.query( - eventId = eventId, - collectionName = CollectionName, - "language".whereEquals(language) - ) + fun getAll(eventId: String, language: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .query("language".whereEquals(language)) - suspend fun createOrUpdate(eventId: String, item: QAndADb) { + fun createOrUpdate(eventId: String, item: QAndADb) { if (item.id == null) { - database.insert( - eventId = eventId, - collectionName = CollectionName - ) { item.copy(id = it) } + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .insert { item.copy(id = it) } } else { - database.update( - eventId = eventId, - collectionName = CollectionName, - id = item.id, - item = item - ) + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .update(item.id, item) } } - suspend fun hasQAndA(eventId: String): Boolean = - database.count(eventId = eventId, collectionName = CollectionName) > 0 + fun hasQAndA(eventId: String): Boolean = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .isNotEmpty() - suspend fun deleteDiff(eventId: String, ids: List) { - val diff = database.diff(eventId, CollectionName, ids) - database.deleteAll(eventId, CollectionName, diff) + fun deleteDiff(eventId: String, ids: List) { + val diff = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .diffRefs(ids) + firestore.batchDelete(diff) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/qanda/QAndAModule.kt b/backend/src/main/java/com/paligot/confily/backend/qanda/QAndAModule.kt index 42de00ba7..7e1938a19 100644 --- a/backend/src/main/java/com/paligot/confily/backend/qanda/QAndAModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/qanda/QAndAModule.kt @@ -1,9 +1,10 @@ package com.paligot.confily.backend.qanda import com.paligot.confily.backend.events.EventModule.eventDao -import com.paligot.confily.backend.internals.InternalModule.database +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore +import com.paligot.confily.backend.internals.SystemEnv.projectName object QAndAModule { - val qAndADao = lazy { QAndADao(database.value) } + val qAndADao = lazy { QAndADao(projectName, cloudFirestore.value) } val qAndARepository = lazy { QAndARepository(eventDao.value, qAndADao.value) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/schedules/ScheduleItemDao.kt b/backend/src/main/java/com/paligot/confily/backend/schedules/ScheduleItemDao.kt index e927a81eb..c6cfb70f3 100644 --- a/backend/src/main/java/com/paligot/confily/backend/schedules/ScheduleItemDao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/schedules/ScheduleItemDao.kt @@ -1,48 +1,63 @@ package com.paligot.confily.backend.schedules -import com.paligot.confily.backend.internals.helpers.database.Database +import com.google.cloud.firestore.Firestore +import com.paligot.confily.backend.internals.helpers.database.batchDelete +import com.paligot.confily.backend.internals.helpers.database.delete import com.paligot.confily.backend.internals.helpers.database.diff -import com.paligot.confily.backend.internals.helpers.database.get -import com.paligot.confily.backend.internals.helpers.database.getAll +import com.paligot.confily.backend.internals.helpers.database.getDocument +import com.paligot.confily.backend.internals.helpers.database.getDocuments +import com.paligot.confily.backend.internals.helpers.database.upsert private const val CollectionName = "schedule-items" -class ScheduleItemDao(private val database: Database) { - suspend fun get(eventId: String, id: String): ScheduleDb? = database.get( - eventId = eventId, - collectionName = CollectionName, - id = id - ) +class ScheduleItemDao( + private val projectName: String, + private val firestore: Firestore +) { + fun get(eventId: String, id: String): ScheduleDb? = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocument(id) - suspend fun getAll(eventId: String): List = database.getAll( - eventId = eventId, - collectionName = CollectionName - ) + fun getAll(eventId: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocuments() - suspend fun createOrUpdate(eventId: String, item: ScheduleDb) { - val existing = database.get(eventId = eventId, collectionName = CollectionName, id = item.id) - if (existing == null) { - database.insert( - eventId = eventId, - collectionName = CollectionName, - id = item.id, - item = item - ) - } else { - database.update(eventId = eventId, collectionName = CollectionName, id = item.id, item = item) - } + fun createOrUpdate(eventId: String, item: ScheduleDb) { + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .upsert(item.id, item) } - suspend fun delete(eventId: String, id: String) = database.delete( - eventId = eventId, - collectionName = CollectionName, - id = id - ) + fun delete(eventId: String, id: String) { + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .delete(id) + } - suspend fun deleteDiff(eventId: String, ids: List) { - val diff = database.diff(eventId, CollectionName, ids) + fun deleteDiff(eventId: String, ids: List) { + val diff = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .diff(ids) // Don't remove break items. .filter { it.talkId != null } - database.deleteAll(eventId, CollectionName, diff.map { it.id }) + firestore.batchDelete( + diff.map { + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .document(it.id) + } + ) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/schedules/ScheduleModule.kt b/backend/src/main/java/com/paligot/confily/backend/schedules/ScheduleModule.kt index 40e312840..f04ffe92a 100644 --- a/backend/src/main/java/com/paligot/confily/backend/schedules/ScheduleModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/schedules/ScheduleModule.kt @@ -3,12 +3,13 @@ package com.paligot.confily.backend.schedules import com.paligot.confily.backend.categories.CategoryModule.categoryDao import com.paligot.confily.backend.events.EventModule.eventDao import com.paligot.confily.backend.formats.FormatModule.formatDao -import com.paligot.confily.backend.internals.InternalModule.database +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore +import com.paligot.confily.backend.internals.SystemEnv.projectName import com.paligot.confily.backend.sessions.SessionModule.sessionDao import com.paligot.confily.backend.speakers.SpeakerModule.speakerDao object ScheduleModule { - val scheduleItemDao = lazy { ScheduleItemDao(database.value) } + val scheduleItemDao = lazy { ScheduleItemDao(projectName, cloudFirestore.value) } val scheduleRepository = lazy { ScheduleRepository( eventDao.value, diff --git a/backend/src/main/java/com/paligot/confily/backend/sessions/SessionDao.kt b/backend/src/main/java/com/paligot/confily/backend/sessions/SessionDao.kt index 2603443fb..ed474422f 100644 --- a/backend/src/main/java/com/paligot/confily/backend/sessions/SessionDao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/sessions/SessionDao.kt @@ -1,8 +1,12 @@ package com.paligot.confily.backend.sessions -import com.paligot.confily.backend.internals.helpers.database.Database -import com.paligot.confily.backend.internals.helpers.database.get -import com.paligot.confily.backend.internals.helpers.database.getAll +import com.google.cloud.firestore.Firestore +import com.paligot.confily.backend.internals.helpers.database.batchDelete +import com.paligot.confily.backend.internals.helpers.database.diffRefs +import com.paligot.confily.backend.internals.helpers.database.getDocument +import com.paligot.confily.backend.internals.helpers.database.getDocuments +import com.paligot.confily.backend.internals.helpers.database.insert +import com.paligot.confily.backend.internals.helpers.database.update import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async @@ -22,17 +26,24 @@ private fun T.getCollectionName() = when (this) { @Suppress("TooManyFunctions") class SessionDao( - private val database: Database, + private val projectName: String, + private val firestore: Firestore, private val dispatcher: CoroutineDispatcher = Dispatchers.IO ) { - suspend fun get(eventId: String, id: String): SessionDb? = + fun get(eventId: String, id: String): SessionDb? = getTalkSession(eventId, id) ?: getEventSession(eventId, id) - suspend fun getTalkSession(eventId: String, id: String): TalkDb? = - database.get(eventId = eventId, collectionName = CollectionName.TALK_SESSIONS, id = id) + fun getTalkSession(eventId: String, id: String): TalkDb? = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName.TALK_SESSIONS) + .getDocument(id) - suspend fun getEventSession(eventId: String, id: String): EventSessionDb? = - database.get(eventId = eventId, collectionName = CollectionName.EVENT_SESSIONS, id = id) + fun getEventSession(eventId: String, id: String): EventSessionDb? = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName.EVENT_SESSIONS) + .getDocument(id) suspend fun getAll(eventId: String): List = coroutineScope { val talkSessions = async(context = dispatcher) { getAllTalkSessions(eventId) } @@ -40,19 +51,24 @@ class SessionDao( return@coroutineScope talkSessions.await() + eventSessions.await() } - suspend fun getAllEventSessions(eventId: String): List = database - .getAll(eventId = eventId, collectionName = CollectionName.EVENT_SESSIONS) + private fun getAllEventSessions(eventId: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName.EVENT_SESSIONS) + .getDocuments() - suspend fun getAllTalkSessions(eventId: String): List = database - .getAll(eventId = eventId, collectionName = CollectionName.TALK_SESSIONS) + fun getAllTalkSessions(eventId: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName.TALK_SESSIONS) + .getDocuments() - suspend fun insert(eventId: String, session: T) { - database.insert( - eventId = eventId, - collectionName = session.getCollectionName(), - id = session.id, - item = session - ) + fun insert(eventId: String, session: T) { + firestore + .collection(projectName) + .document(eventId) + .collection(session.getCollectionName()) + .insert(session.id, session) } suspend fun insertAll(eventId: String, talks: List) = coroutineScope { @@ -63,28 +79,27 @@ class SessionDao( Unit } - suspend fun update(eventId: String, session: T) { - database.update( - eventId = eventId, - collectionName = session.getCollectionName(), - id = session.id, - item = session - ) + fun update(eventId: String, session: T) { + firestore + .collection(projectName) + .document(eventId) + .collection(session.getCollectionName()) + .update(session.id, session) } - suspend fun createOrUpdate(eventId: String, session: T): String { + fun createOrUpdate(eventId: String, session: T): String { if (session.id == "") { - return database.insert( - eventId = eventId, - collectionName = session.getCollectionName(), - transform = { + return firestore + .collection(projectName) + .document(eventId) + .collection(session.getCollectionName()) + .insert { when (session) { is TalkDb -> session.copy(it) is EventSessionDb -> session.copy(it) else -> TODO("Session not implemented") } } - ) } val existing = get(eventId, session.id) if (existing == null) { @@ -95,13 +110,21 @@ class SessionDao( return session.id } - suspend fun deleteDiffTalkSessions(eventId: String, ids: List) { - val diff = database.diff(eventId, CollectionName.TALK_SESSIONS, ids) - database.deleteAll(eventId, CollectionName.TALK_SESSIONS, diff) + fun deleteDiffTalkSessions(eventId: String, ids: List) { + val diff = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName.TALK_SESSIONS) + .diffRefs(ids) + firestore.batchDelete(diff) } - suspend fun deleteDiffEventSessions(eventId: String, ids: List) { - val diff = database.diff(eventId, CollectionName.EVENT_SESSIONS, ids) - database.deleteAll(eventId, CollectionName.EVENT_SESSIONS, diff) + fun deleteDiffEventSessions(eventId: String, ids: List) { + val diff = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName.EVENT_SESSIONS) + .diffRefs(ids) + firestore.batchDelete(diff) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/sessions/SessionModule.kt b/backend/src/main/java/com/paligot/confily/backend/sessions/SessionModule.kt index 73057599f..090a37fbb 100644 --- a/backend/src/main/java/com/paligot/confily/backend/sessions/SessionModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/sessions/SessionModule.kt @@ -1,11 +1,12 @@ package com.paligot.confily.backend.sessions import com.paligot.confily.backend.events.EventModule.eventDao -import com.paligot.confily.backend.internals.InternalModule.database +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore +import com.paligot.confily.backend.internals.SystemEnv.projectName import com.paligot.confily.backend.third.parties.geocode.GeocodeModule.geocodeApi object SessionModule { - val sessionDao = lazy { SessionDao(database.value) } + val sessionDao = lazy { SessionDao(projectName, cloudFirestore.value) } val sessionRepository = lazy { SessionRepository(geocodeApi.value, eventDao.value, sessionDao.value) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/speakers/SpeakerDao.kt b/backend/src/main/java/com/paligot/confily/backend/speakers/SpeakerDao.kt index eddf8978f..ac9a53035 100644 --- a/backend/src/main/java/com/paligot/confily/backend/speakers/SpeakerDao.kt +++ b/backend/src/main/java/com/paligot/confily/backend/speakers/SpeakerDao.kt @@ -1,9 +1,13 @@ package com.paligot.confily.backend.speakers -import com.paligot.confily.backend.internals.helpers.database.Database -import com.paligot.confily.backend.internals.helpers.database.get -import com.paligot.confily.backend.internals.helpers.database.getAll +import com.google.cloud.firestore.Firestore +import com.paligot.confily.backend.internals.helpers.database.batchDelete +import com.paligot.confily.backend.internals.helpers.database.diffRefs +import com.paligot.confily.backend.internals.helpers.database.getDocument +import com.paligot.confily.backend.internals.helpers.database.getDocuments +import com.paligot.confily.backend.internals.helpers.database.insert import com.paligot.confily.backend.internals.helpers.database.query +import com.paligot.confily.backend.internals.helpers.database.upsert import com.paligot.confily.backend.internals.helpers.database.whereIn import com.paligot.confily.backend.internals.helpers.storage.MimeType import com.paligot.confily.backend.internals.helpers.storage.Storage @@ -13,72 +17,70 @@ import kotlinx.coroutines.coroutineScope private const val CollectionName = "speakers" -class SpeakerDao(private val database: Database, private val storage: Storage) { - suspend fun get(eventId: String, id: String): SpeakerDb? = database.get( - eventId = eventId, - collectionName = CollectionName, - id = id - ) +class SpeakerDao( + private val projectName: String, + private val firestore: Firestore, + private val storage: Storage +) { + fun get(eventId: String, id: String): SpeakerDb? = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocument(id) - suspend fun getByIds(eventId: String, ids: List): List = + fun getByIds(eventId: String, ids: List): List = try { - database.query( - eventId = eventId, - collectionName = CollectionName, - "id".whereIn(ids) - ) + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .query("id".whereIn(ids)) } catch (ignored: Throwable) { emptyList() } - suspend fun getAll(eventId: String): List = database.getAll( - eventId = eventId, - collectionName = CollectionName - ) + fun getAll(eventId: String): List = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .getDocuments() - suspend fun insert(eventId: String, speaker: SpeakerDb) = coroutineScope { - database.insert(eventId = eventId, collectionName = CollectionName, id = speaker.id, item = speaker) + fun insert(eventId: String, speaker: SpeakerDb) { + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .insert(speaker.id, speaker) } suspend fun insertAll(eventId: String, speakers: List) = coroutineScope { val asyncItems = speakers.map { async { - database.insert( - eventId = eventId, - collectionName = CollectionName, - id = it.id, - item = it - ) + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .insert(it.id, it) } } asyncItems.awaitAll() Unit } - suspend fun createOrUpdate(eventId: String, speaker: SpeakerDb): String = coroutineScope { + fun createOrUpdate(eventId: String, speaker: SpeakerDb): String { if (speaker.id == "") { - return@coroutineScope database.insert( - eventId = eventId, - collectionName = CollectionName - ) { speaker.copy(id = it) } + return firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .insert { speaker.copy(id = it) } } - val existing = database.get(eventId = eventId, collectionName = CollectionName, id = speaker.id) - if (existing == null) { - database.insert( - eventId = eventId, - collectionName = CollectionName, - id = speaker.id, - item = speaker - ) - } else { - database.update( - eventId = eventId, - collectionName = CollectionName, - id = speaker.id, - item = speaker - ) - } - return@coroutineScope speaker.id + firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .upsert(speaker.id, speaker) + return speaker.id } suspend fun saveProfile(eventId: String, id: String, content: ByteArray, mimeType: MimeType) = @@ -88,8 +90,12 @@ class SpeakerDao(private val database: Database, private val storage: Storage) { mimeType = mimeType ) - suspend fun deleteDiff(eventId: String, ids: List) { - val diff = database.diff(eventId, CollectionName, ids) - database.deleteAll(eventId, CollectionName, diff) + fun deleteDiff(eventId: String, ids: List) { + val diff = firestore + .collection(projectName) + .document(eventId) + .collection(CollectionName) + .diffRefs(ids) + firestore.batchDelete(diff) } } diff --git a/backend/src/main/java/com/paligot/confily/backend/speakers/SpeakerModule.kt b/backend/src/main/java/com/paligot/confily/backend/speakers/SpeakerModule.kt index cf93ad152..1d53c9564 100644 --- a/backend/src/main/java/com/paligot/confily/backend/speakers/SpeakerModule.kt +++ b/backend/src/main/java/com/paligot/confily/backend/speakers/SpeakerModule.kt @@ -1,12 +1,13 @@ package com.paligot.confily.backend.speakers import com.paligot.confily.backend.events.EventModule.eventDao +import com.paligot.confily.backend.internals.GoogleServicesModule.cloudFirestore import com.paligot.confily.backend.internals.InternalModule.commonApi -import com.paligot.confily.backend.internals.InternalModule.database import com.paligot.confily.backend.internals.InternalModule.storage +import com.paligot.confily.backend.internals.SystemEnv.projectName object SpeakerModule { - val speakerDao = lazy { SpeakerDao(database.value, storage.value) } + val speakerDao = lazy { SpeakerDao(projectName, cloudFirestore.value, storage.value) } val speakerRepository = lazy { SpeakerRepository(commonApi.value, eventDao.value, speakerDao.value) } }