diff --git a/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/SharedApiUrlCreator.kt b/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/SharedApiUrlCreator.kt index 8ed45557..03b68efd 100644 --- a/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/SharedApiUrlCreator.kt +++ b/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/SharedApiUrlCreator.kt @@ -21,6 +21,7 @@ import com.infomaniak.multiplatform_swisstransfer.common.exceptions.RealmExcepti import com.infomaniak.multiplatform_swisstransfer.database.controllers.TransferController import com.infomaniak.multiplatform_swisstransfer.database.controllers.UploadController import com.infomaniak.multiplatform_swisstransfer.network.utils.SharedApiRoutes +import kotlin.coroutines.cancellation.CancellationException /** * Utility class responsible for creating API URLs for shared routes. @@ -33,14 +34,14 @@ class SharedApiUrlCreator internal constructor( fun shareTransferUrl(transferUUID: String) = SharedApiRoutes.shareTransfer(transferUUID) - @Throws(RealmException::class) - fun downloadFilesUrl(transferUUID: String): String? { + @Throws(RealmException::class, CancellationException::class) + suspend fun downloadFilesUrl(transferUUID: String): String? { val transfer = transferController.getTransfer(transferUUID) ?: return null return SharedApiRoutes.downloadFiles(transfer.downloadHost, transfer.linkUUID) } - @Throws(RealmException::class) - fun downloadFileUrl(transferUUID: String, fileUUID: String?): String? { + @Throws(RealmException::class, CancellationException::class) + suspend fun downloadFileUrl(transferUUID: String, fileUUID: String?): String? { val transfer = transferController.getTransfer(transferUUID) ?: return null return SharedApiRoutes.downloadFile(transfer.downloadHost, transfer.linkUUID, fileUUID) } diff --git a/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/managers/AccountManager.kt b/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/managers/AccountManager.kt index fda01dae..d35de07f 100644 --- a/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/managers/AccountManager.kt +++ b/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/managers/AccountManager.kt @@ -47,7 +47,7 @@ class AccountManager internal constructor( @Throws(RealmException::class, CancellationException::class) suspend fun loadUser(userId: Int) { appSettingsController.initAppSettings(emailLanguageUtils.getEmailLanguageFromLocal()) - realmProvider.openRealmTransfers(userId) + realmProvider.openTransfersDb(userId) } /** @@ -60,6 +60,6 @@ class AccountManager internal constructor( uploadController.removeData() transferController.removeData() - realmProvider.closeAllRealms() + realmProvider.closeAllDatabases() } } diff --git a/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/managers/TransferManager.kt b/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/managers/TransferManager.kt index 82539e55..96b30a02 100644 --- a/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/managers/TransferManager.kt +++ b/STCore/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/managers/TransferManager.kt @@ -122,7 +122,7 @@ class TransferManager internal constructor( * * @return A transfer matching the specified transferUUID or null. */ - fun getTransferByUUID(transferUUID: String): TransferUi? { + suspend fun getTransferByUUID(transferUUID: String): TransferUi? { return transferController.getTransfer(transferUUID)?.let(::TransferUi) } diff --git a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/RealmProvider.kt b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/RealmProvider.kt index be5d02b0..145bbdb8 100644 --- a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/RealmProvider.kt +++ b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/RealmProvider.kt @@ -26,36 +26,53 @@ import com.infomaniak.multiplatform_swisstransfer.database.models.upload.RemoteU import com.infomaniak.multiplatform_swisstransfer.database.models.upload.UploadContainerDB import com.infomaniak.multiplatform_swisstransfer.database.models.upload.UploadFileSessionDB import com.infomaniak.multiplatform_swisstransfer.database.models.upload.UploadSessionDB +import com.infomaniak.multiplatform_swisstransfer.database.utils.RealmUtils.runThrowingRealm import io.realm.kotlin.Realm import io.realm.kotlin.RealmConfiguration +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.flow class RealmProvider(private val loadDataInMemory: Boolean = false) { - val realmAppSettings by lazy { Realm.open(realmAppSettingsConfiguration) } - val realmUploads by lazy { Realm.open(realmUploadDBConfiguration) } - var realmTransfers: Realm? = null - private set + val appSettings by lazy { Realm.open(realmAppSettingsConfiguration) } + val uploads by lazy { Realm.open(realmUploadDBConfiguration) } + private val transfersAsync = CompletableDeferred() + private suspend fun transfers(): Realm = transfersAsync.await() - fun openRealmTransfers(userId: Int) { - realmTransfers = Realm.open(realmTransfersConfiguration(userId)) + fun openTransfersDb(userId: Int) { + transfersAsync.complete(Realm.open(realmTransfersConfiguration(userId))) } - fun closeRealmAppSettings() { - realmAppSettings.close() + internal suspend inline fun withTransfersDb(block: (Realm) -> T): T { + runThrowingRealm { + return block(transfers()) + } } - fun closeRealmUploads() { - realmUploads.close() + internal fun flowWithTransfersDb(block: suspend (Realm) -> Flow): Flow = flow { + runThrowingRealm { + emitAll(block(transfers())) + } } - fun closeRealmTransfers() { - realmTransfers?.close() + fun closeAppSettingsDb() { + appSettings.close() } - fun closeAllRealms() { - closeRealmAppSettings() - closeRealmUploads() - closeRealmTransfers() + fun closeUploadsDb() { + uploads.close() + } + + suspend fun closeTransfersDb() { + transfersAsync.await().close() + } + + suspend fun closeAllDatabases() { + closeAppSettingsDb() + closeUploadsDb() + closeTransfersDb() } private val realmAppSettingsConfiguration = RealmConfiguration diff --git a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/AppSettingsController.kt b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/AppSettingsController.kt index 4fce1a5d..60a621cf 100644 --- a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/AppSettingsController.kt +++ b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/AppSettingsController.kt @@ -33,7 +33,7 @@ import kotlin.coroutines.cancellation.CancellationException @OptIn(ExperimentalCoroutinesApi::class) class AppSettingsController(private val realmProvider: RealmProvider) { - private val realm by lazy { realmProvider.realmAppSettings } + private val realm by lazy { realmProvider.appSettings } private val appSettingsQuery get() = realm.query().first() diff --git a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/EmailTokensController.kt b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/EmailTokensController.kt index 80c71e42..5e5aef31 100644 --- a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/EmailTokensController.kt +++ b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/EmailTokensController.kt @@ -27,7 +27,7 @@ import io.realm.kotlin.ext.query import kotlin.coroutines.cancellation.CancellationException class EmailTokensController(private val realmProvider: RealmProvider) { - private val realm by lazy { realmProvider.realmAppSettings } + private val realm by lazy { realmProvider.appSettings } //region Get data @Throws(RealmException::class) diff --git a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/FileController.kt b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/FileController.kt index ed578f89..4f0641c2 100644 --- a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/FileController.kt +++ b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/FileController.kt @@ -21,7 +21,6 @@ import com.infomaniak.multiplatform_swisstransfer.common.exceptions.RealmExcepti import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.File import com.infomaniak.multiplatform_swisstransfer.database.RealmProvider import com.infomaniak.multiplatform_swisstransfer.database.models.transfers.FileDB -import com.infomaniak.multiplatform_swisstransfer.database.utils.RealmUtils.runThrowingRealm import io.realm.kotlin.ext.query import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -30,11 +29,9 @@ import kotlinx.coroutines.flow.mapLatest @OptIn(ExperimentalCoroutinesApi::class) class FileController(private val realmProvider: RealmProvider) { - private val realm by lazy { realmProvider.realmTransfers!! } - @Throws(RealmException::class) - fun getFilesFromTransfer(folderUuid: String): Flow> = runThrowingRealm { + fun getFilesFromTransfer(folderUuid: String): Flow> = realmProvider.flowWithTransfersDb { realm -> val query = "${FileDB::folder.name}.uuid == '$folderUuid'" - return realm.query(query).asFlow().mapLatest { it.list } + realm.query(query).asFlow().mapLatest { it.list } } } diff --git a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/TransferController.kt b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/TransferController.kt index acd3c2d3..4d65b8f2 100644 --- a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/TransferController.kt +++ b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/TransferController.kt @@ -27,7 +27,6 @@ import com.infomaniak.multiplatform_swisstransfer.common.utils.DateUtils import com.infomaniak.multiplatform_swisstransfer.database.RealmProvider import com.infomaniak.multiplatform_swisstransfer.database.models.transfers.TransferDB import com.infomaniak.multiplatform_swisstransfer.database.utils.FileUtils -import com.infomaniak.multiplatform_swisstransfer.database.utils.RealmUtils.runThrowingRealm import io.realm.kotlin.MutableRealm import io.realm.kotlin.Realm import io.realm.kotlin.UpdatePolicy @@ -43,11 +42,11 @@ import kotlin.coroutines.cancellation.CancellationException @OptIn(ExperimentalCoroutinesApi::class) class TransferController(private val realmProvider: RealmProvider) { - private val realm by lazy { realmProvider.realmTransfers!! } - //region Get data - @Throws(RealmException::class) - internal fun getTransfers(transferDirection: TransferDirection? = null): RealmResults = runThrowingRealm { + @Throws(RealmException::class, CancellationException::class) + internal suspend fun getTransfers( + transferDirection: TransferDirection? = null + ): RealmResults = realmProvider.withTransfersDb { realm -> val directionFilterQuery = when (transferDirection) { null -> TRUE_PREDICATE else -> "${TransferDB.transferDirectionPropertyName} == '${transferDirection}'" @@ -56,22 +55,22 @@ class TransferController(private val realmProvider: RealmProvider) { } @Throws(RealmException::class) - fun getTransfersFlow(transferDirection: TransferDirection): Flow> = runThrowingRealm { - return getTransfers(transferDirection).asFlow().mapLatest { it.list } + fun getTransfersFlow(transferDirection: TransferDirection): Flow> = realmProvider.flowWithTransfersDb { + getTransfers(transferDirection).asFlow().mapLatest { it.list } } @Throws(RealmException::class) - fun getTransferFlow(linkUUID: String): Flow = runThrowingRealm { - return getTransferQuery(realm, linkUUID).asFlow().mapLatest { it.obj } + fun getTransferFlow(linkUUID: String): Flow = realmProvider.flowWithTransfersDb { realm -> + getTransferQuery(realm, linkUUID).asFlow().mapLatest { it.obj } } - @Throws(RealmException::class) - fun getTransfer(linkUUID: String): Transfer? = runThrowingRealm { + @Throws(RealmException::class, CancellationException::class) + suspend fun getTransfer(linkUUID: String): Transfer? = realmProvider.withTransfersDb { realm -> return getTransferQuery(realm, linkUUID).find() } - @Throws(RealmException::class) - fun getNotReadyTransfers(): List = runThrowingRealm { + @Throws(RealmException::class, CancellationException::class) + suspend fun getNotReadyTransfers(): List = realmProvider.withTransfersDb { realm -> val query = "${TransferDB.transferStatusPropertyName} != '${TransferStatus.READY.name}'" return realm.query(query).find() } @@ -79,7 +78,7 @@ class TransferController(private val realmProvider: RealmProvider) { //region Upsert data @Throws(RealmException::class, CancellationException::class, TransferWithoutFilesException::class) - suspend fun upsert(transfer: Transfer, transferDirection: TransferDirection, password: String?) = runThrowingRealm { + suspend fun upsert(transfer: Transfer, transferDirection: TransferDirection, password: String?) = realmProvider.withTransfersDb { realm -> realm.write { val transferDB = TransferDB(transfer, transferDirection, password) transferDB.container?.files?.let { transferFiles -> @@ -94,7 +93,7 @@ class TransferController(private val realmProvider: RealmProvider) { linkUUID: String, uploadSession: UploadSession, transferStatus: TransferStatus, - ) = runThrowingRealm { + ) = realmProvider.withTransfersDb { realm -> val transferDB = TransferDB(linkUUID, uploadSession, transferStatus).apply { container?.files?.let { files -> FileUtils.getFileDBTree(containerUUID, files) @@ -109,7 +108,7 @@ class TransferController(private val realmProvider: RealmProvider) { //region Update data @Throws(RealmException::class, CancellationException::class) - suspend fun deleteTransfer(transferUUID: String) = runThrowingRealm { + suspend fun deleteTransfer(transferUUID: String) = realmProvider.withTransfersDb { realm -> realm.write { val transferToDelete = query("${TransferDB::linkUUID.name} == '$transferUUID'").first() delete(transferToDelete) @@ -117,12 +116,12 @@ class TransferController(private val realmProvider: RealmProvider) { } @Throws(RealmException::class, CancellationException::class) - suspend fun deleteExpiredTransfers() = runThrowingRealm { + suspend fun deleteExpiredTransfers() = realmProvider.withTransfersDb { realm -> realm.write { delete(getExpiredTransfersQuery(realm = this)) } } @Throws(RealmException::class, CancellationException::class) - suspend fun removeData() = runThrowingRealm { + suspend fun removeData() = realmProvider.withTransfersDb { realm -> realm.write { deleteAll() } } //endregion diff --git a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/UploadController.kt b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/UploadController.kt index 863c1786..8220da6f 100644 --- a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/UploadController.kt +++ b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/controllers/UploadController.kt @@ -35,7 +35,7 @@ import kotlin.coroutines.cancellation.CancellationException class UploadController(private val realmProvider: RealmProvider) { - private val realm by lazy { realmProvider.realmUploads } + private val realm by lazy { realmProvider.uploads } //region Queries private fun getUploadsQuery() = realm.query() diff --git a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/utils/RealmUtils.kt b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/utils/RealmUtils.kt index 73e40ba0..048b3a3b 100644 --- a/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/utils/RealmUtils.kt +++ b/STDatabase/src/commonMain/kotlin/com/infomaniak/multiplatform_swisstransfer/database/utils/RealmUtils.kt @@ -18,11 +18,14 @@ package com.infomaniak.multiplatform_swisstransfer.database.utils import com.infomaniak.multiplatform_swisstransfer.common.exceptions.RealmException +import kotlinx.coroutines.CancellationException object RealmUtils { @Throws(RealmException::class) inline fun runThrowingRealm(block: () -> R): R { - return runCatching { block() }.getOrElse { throw RealmException(it) } + return runCatching { block() }.onFailure { + if (it is CancellationException) throw it + }.getOrElse { throw RealmException(it) } } } diff --git a/STDatabase/src/commonTest/kotlin/com/infomaniak/multiplatform_swisstransfer/database/TransferControllerTest.kt b/STDatabase/src/commonTest/kotlin/com/infomaniak/multiplatform_swisstransfer/database/TransferControllerTest.kt index 1bc93f43..e68b8a2f 100644 --- a/STDatabase/src/commonTest/kotlin/com/infomaniak/multiplatform_swisstransfer/database/TransferControllerTest.kt +++ b/STDatabase/src/commonTest/kotlin/com/infomaniak/multiplatform_swisstransfer/database/TransferControllerTest.kt @@ -32,14 +32,14 @@ class TransferControllerTest { @BeforeTest fun setup() { - realmProvider = RealmProvider(loadDataInMemory = true).apply { openRealmTransfers(userId = 0) } + realmProvider = RealmProvider(loadDataInMemory = true).apply { openTransfersDb(userId = 0) } transferController = TransferController(realmProvider) } @AfterTest fun tearDown() = runTest { transferController.removeData() - realmProvider.closeRealmTransfers() + realmProvider.closeTransfersDb() } @Test diff --git a/STDatabase/src/commonTest/kotlin/com/infomaniak/multiplatform_swisstransfer/database/UploadControllerTest.kt b/STDatabase/src/commonTest/kotlin/com/infomaniak/multiplatform_swisstransfer/database/UploadControllerTest.kt index 3ee2b0c0..0e0f20df 100644 --- a/STDatabase/src/commonTest/kotlin/com/infomaniak/multiplatform_swisstransfer/database/UploadControllerTest.kt +++ b/STDatabase/src/commonTest/kotlin/com/infomaniak/multiplatform_swisstransfer/database/UploadControllerTest.kt @@ -37,7 +37,7 @@ class UploadControllerTest { @AfterTest fun tearDown() = runTest { uploadController.removeData() - realmProvider.closeRealmUploads() + realmProvider.closeUploadsDb() } @Test