Skip to content

Commit

Permalink
Merge pull request #31 from Infomaniak/transfer-manager
Browse files Browse the repository at this point in the history
feat: Transfer manager
  • Loading branch information
tevincent authored Oct 15, 2024
2 parents 484303d + c5e0ebc commit 519bb76
Show file tree
Hide file tree
Showing 22 changed files with 383 additions and 75 deletions.
3 changes: 1 addition & 2 deletions STCore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ centralized access point to orchestrate transfer operations.

- **Type**: `TransferManager`
- **Description**:
- `transferManager` is a lazily initialized property that provides a manager to orchestrate all transfer operations. It
uses `realmProvider` and `apiClientProvider` to configure and manage Transfers efficiently.
- `transferManager` is a lazily initialized property that provides a manager to orchestrate all transfer operations.

- **Usage Example**:
```kotlin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package com.infomaniak.multiplatform_swisstransfer

import com.infomaniak.multiplatform_swisstransfer.database.RealmProvider
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.AppSettingsController
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.TransfersController
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.TransferController
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.UploadController
import com.infomaniak.multiplatform_swisstransfer.managers.AccountManager
import com.infomaniak.multiplatform_swisstransfer.managers.AppSettingsManager
Expand Down Expand Up @@ -49,14 +49,14 @@ class SwissTransferInjection {

private val appSettingsController by lazy { AppSettingsController(realmProvider) }
private val uploadController by lazy { UploadController(realmProvider) }
private val transfersController by lazy { TransfersController(realmProvider) }
private val transferController by lazy { TransferController(realmProvider) }

/** A manager used to orchestrate Transfers operations. */
val transferManager by lazy { TransferManager(realmProvider, apiClientProvider) }
val transferManager by lazy { TransferManager(apiClientProvider, transferController, transferRepository) }

/** A manager used to orchestrate AppSettings operations. */
val appSettingsManager by lazy { AppSettingsManager(appSettingsController) }

/** A manager used to orchestrate Accounts operations. */
val accountManager by lazy { AccountManager(appSettingsController, uploadController, transfersController, realmProvider) }
val accountManager by lazy { AccountManager(appSettingsController, uploadController, transferController, realmProvider) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,21 @@ package com.infomaniak.multiplatform_swisstransfer.managers

import com.infomaniak.multiplatform_swisstransfer.database.RealmProvider
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.AppSettingsController
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.TransfersController
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.TransferController
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.UploadController

/**
* AccountManager is responsible for orchestrating Accounts operations using Realm for local data management.
*
* @property appSettingsController The controller for managing AppSettings operations.
* @property uploadController The controller for managing Upload operations.
* @property transfersController The controller for managing Transfers operation.
* @property transferController The controller for managing Transfers operation.
* @property realmProvider The provider for managing Realm database operations.
*/
class AccountManager internal constructor(
private val appSettingsController: AppSettingsController,
private val uploadController: UploadController,
private val transfersController: TransfersController,
private val transferController: TransferController,
private val realmProvider: RealmProvider,
) {

Expand All @@ -54,7 +54,7 @@ class AccountManager internal constructor(

appSettingsController.removeData()
uploadController.removeData()
transfersController.removeData()
transferController.removeData()

realmProvider.closeAllRealms()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,20 @@
*/
package com.infomaniak.multiplatform_swisstransfer.managers

import com.infomaniak.multiplatform_swisstransfer.database.RealmProvider
import com.infomaniak.multiplatform_swisstransfer.common.exceptions.UnknownException
import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.Transfer
import com.infomaniak.multiplatform_swisstransfer.database.cache.setting.TransferController
import com.infomaniak.multiplatform_swisstransfer.network.ApiClientProvider
import com.infomaniak.multiplatform_swisstransfer.network.exceptions.ApiException
import com.infomaniak.multiplatform_swisstransfer.network.exceptions.NetworkException
import com.infomaniak.multiplatform_swisstransfer.network.exceptions.UnexpectedApiErrorFormatException
import com.infomaniak.multiplatform_swisstransfer.network.models.transfer.TransferApi
import com.infomaniak.multiplatform_swisstransfer.network.repositories.TransferRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import kotlin.coroutines.cancellation.CancellationException

/**
* TransferManager is responsible for orchestrating data transfer operations
Expand All @@ -28,12 +40,82 @@ import com.infomaniak.multiplatform_swisstransfer.network.ApiClientProvider
* smooth and efficient data transfers, providing a centralized management point
* for transfer-related activities.
*
* @property realmProvider The provider for managing Realm database operations.
* @property clientProvider The provider for creating and configuring HTTP clients for API communication.
* @property transferController The provider for transfer data from database.
* @property transferRepository The provider for transfer data from api.
*/
class TransferManager internal constructor(
private val realmProvider: RealmProvider,
private val clientProvider: ApiClientProvider,
private val transferController: TransferController,
private val transferRepository: TransferRepository,
) {
// TODO: Implement here

/**
* The `Flow` of [transfers] is used to receive updates for new transfers added in database.
* @see addTransferByLinkUuid
* @see addTransferByUrl
*/
val transfers get() = transferController.getTransfersFlow().flowOn(Dispatchers.IO)

/**
* Retrieves a transfer using the provided link UUID and saves it to the database.
*
* This function is typically used after a transfer has been uploaded. Once the upload is complete,
* a `linkUuid` is returned, which must be passed to this function to retrieve the corresponding transfer.
* After retrieving the transfer, it is saved to the database.
*
* @see transfers
*
* @param linkUuid The UUID corresponding to the uploaded transfer link.
* @throws CancellationException If the operation is cancelled.
* @throws ApiException If there is an error related to the API during transfer retrieval.
* @throws UnexpectedApiErrorFormatException Unparsable api error response.
* @throws NetworkException If there is a network issue during the transfer retrieval.
* @throws UnknownException Any error not already handled by the above ones.
*/
@Throws(
CancellationException::class,
ApiException::class,
UnexpectedApiErrorFormatException::class,
NetworkException::class,
UnknownException::class,
)
suspend fun addTransferByLinkUuid(linkUuid: String) = withContext(Dispatchers.IO) {
addTransfer(transferRepository.getTransferByLinkUuid(linkUuid).data)
}

/**
* Retrieves a transfer using the provided URL and saves it to the database.
*
* This function is used when a transfer URL is available. The provided `url` is used to retrieve
* the corresponding transfer, and after the transfer is successfully retrieved, it is saved to
* the database.
*
* @see transfers
*
* @param url The URL associated with the transfer to retrieve.
* @throws CancellationException If the operation is cancelled.
* @throws ApiException If there is an error related to the API during transfer retrieval.
* @throws UnexpectedApiErrorFormatException Unparsable api error response.
* @throws NetworkException If there is a network issue during the transfer retrieval.
* @throws UnknownException Any error not already handled by the above ones.
*/
@Throws(
CancellationException::class,
ApiException::class,
UnexpectedApiErrorFormatException::class,
NetworkException::class,
UnknownException::class,
)
suspend fun addTransferByUrl(url: String) = withContext(Dispatchers.IO) {
addTransfer(transferRepository.getTransferByUrl(url).data)
}

private suspend fun addTransfer(transferApi: TransferApi?) {
runCatching {
transferController.upsert(transferApi as Transfer<*>)
}.onFailure {
throw UnknownException(it)
}
}
}
1 change: 1 addition & 0 deletions STDatabase/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ kotlin {
}
commonTest.dependencies {
implementation(libs.kotlin.test)
implementation(libs.kotlinx.coroutines.test)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ class RealmProvider {
var realmTransfers: Realm? = null
private set

fun openRealmTransfers(userId: Int) {
realmTransfers = Realm.open(realmTransfersConfiguration(userId))
fun openRealmTransfers(userId: Int, inMemory: Boolean = false) {
realmTransfers = Realm.open(realmTransfersConfiguration(userId, inMemory))
}

fun closeRealmAppSettings() {
Expand Down Expand Up @@ -64,9 +64,10 @@ class RealmProvider {
.name("Uploads")
.build()

private fun realmTransfersConfiguration(userId: Int) = RealmConfiguration
private fun realmTransfersConfiguration(userId: Int, inMemory: Boolean) = RealmConfiguration
.Builder(schema = setOf(TransferDB::class, ContainerDB::class, FileDB::class))
.name(transferRealmName(userId))
.apply { if (inMemory) inMemory() }
.build()

private fun transferRealmName(userId: Int) = "Transfers-$userId"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,44 @@
*/
package com.infomaniak.multiplatform_swisstransfer.database.cache.setting

import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.Transfer
import com.infomaniak.multiplatform_swisstransfer.database.RealmProvider
import com.infomaniak.multiplatform_swisstransfer.database.models.transfers.TransferDB
import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.query
import io.realm.kotlin.query.RealmResults
import io.realm.kotlin.query.Sort
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.mapLatest
import kotlin.coroutines.cancellation.CancellationException

class TransfersController(private val realmProvider: RealmProvider) {
@OptIn(ExperimentalCoroutinesApi::class)
class TransferController(private val realmProvider: RealmProvider) {

private val realm by lazy { realmProvider.realmTransfers }

//region Get data
@Throws(IllegalArgumentException::class, CancellationException::class)
fun getTransfers(): RealmResults<TransferDB>? = realm?.query<TransferDB>()?.find()
fun getTransfers(): RealmResults<TransferDB>? {
return realm?.query<TransferDB>()?.sort(TransferDB::createdDateTimestamp.name, Sort.DESCENDING)?.find()
}

@Throws(IllegalArgumentException::class, CancellationException::class)
fun getTransfersFlow(): Flow<List<TransferDB>> = getTransfers()?.asFlow()?.mapLatest { it.list } ?: emptyFlow()

fun getTransfer(linkUuid: String): TransferDB? {
return realm?.query<TransferDB>("${TransferDB::linkUuid.name} == '$linkUuid'")?.first()?.find()
}
//endregion

//region Upsert data
suspend fun upsert(transfer: Transfer<*>) {
realm?.write {
this.copyToRealm(TransferDB(transfer), UpdatePolicy.ALL)
}
}
//endregion

//region Update data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
package com.infomaniak.multiplatform_swisstransfer.database.models.transfers

import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.Container
import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.File
import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey

class ContainerDB : Container<RealmList<FileDB>>, RealmObject {
class ContainerDB() : Container<RealmList<FileDB>>, RealmObject {
@PrimaryKey
override var uuid: String = ""
override var duration: Long = 0L
Expand All @@ -41,4 +42,21 @@ class ContainerDB : Container<RealmList<FileDB>>, RealmObject {
// @SerialName("WSUser") // TODO: What's this ?
// val wsUser: JsonElement?
override var files: RealmList<FileDB> = realmListOf()

constructor(container: Container<List<File>>) : this() {
this.uuid = container.uuid
this.duration = container.duration
this.createdDateTimestamp = container.createdDateTimestamp
this.expiredDateTimestamp = container.expiredDateTimestamp
this.numberOfFiles = container.numberOfFiles
this.message = container.message
this.needPassword = container.needPassword
this.language = container.language
this.sizeUploaded = container.sizeUploaded
this.deletedDateTimestamp = container.deletedDateTimestamp
this.swiftVersion = container.swiftVersion
this.downloadLimit = container.downloadLimit
this.source = container.source
this.files = container.files.mapTo(realmListOf()) { FileDB(it) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.Fi
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey

class FileDB : File, RealmObject {
class FileDB() : File, RealmObject {
@PrimaryKey
override var containerUuid: String = ""
override var uuid: String = ""
Expand All @@ -36,4 +36,20 @@ class FileDB : File, RealmObject {
override var receivedSizeInBytes: Long = 0L
override var path: String? = ""
override var thumbnailPath: String? = ""

constructor(file: File) : this() {
this.containerUuid = file.containerUuid
this.uuid = file.uuid
this.fileName = file.fileName
this.fileSizeInBytes = file.fileSizeInBytes
this.downloadCounter = file.downloadCounter
this.createdDateTimestamp = file.createdDateTimestamp
this.expiredDateTimestamp = file.expiredDateTimestamp
this.eVirus = file.eVirus
this.deletedDate = file.deletedDate
this.mimeType = file.mimeType
this.receivedSizeInBytes = file.receivedSizeInBytes
this.path = file.path
this.thumbnailPath = file.thumbnailPath
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@
*/
package com.infomaniak.multiplatform_swisstransfer.database.models.transfers

import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.Container
import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.File
import com.infomaniak.multiplatform_swisstransfer.common.interfaces.transfers.Transfer
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey

class TransferDB : Transfer<ContainerDB?>, RealmObject {
class TransferDB() : Transfer<ContainerDB?>, RealmObject {
@PrimaryKey
override var linkUuid: String = ""
override var containerUuid: String = ""
Expand All @@ -32,4 +34,17 @@ class TransferDB : Transfer<ContainerDB?>, RealmObject {
override var isMailSent: Boolean = false
override var downloadHost: String = ""
override var container: ContainerDB? = null

@Suppress("UNCHECKED_CAST")
constructor(transfer: Transfer<*>) : this() {
this.linkUuid = transfer.linkUuid
this.containerUuid = transfer.containerUuid
this.downloadCounterCredit = transfer.downloadCounterCredit
this.createdDateTimestamp = transfer.createdDateTimestamp
this.expiredDateTimestamp = transfer.expiredDateTimestamp
this.hasBeenDownloadedOneTime = transfer.hasBeenDownloadedOneTime
this.isMailSent = transfer.isMailSent
this.downloadHost = transfer.downloadHost
this.container = ContainerDB(transfer.container as Container<List<File>>)
}
}

This file was deleted.

Loading

0 comments on commit 519bb76

Please sign in to comment.