diff --git a/desktopData/src/main/kotlin/com/intelligentbackpack/desktopdata/datasource/DesktopDomainRepositoryImpl.kt b/desktopData/src/main/kotlin/com/intelligentbackpack/desktopdata/datasource/DesktopDomainRepositoryImpl.kt index 0e86e831..4b877941 100644 --- a/desktopData/src/main/kotlin/com/intelligentbackpack/desktopdata/datasource/DesktopDomainRepositoryImpl.kt +++ b/desktopData/src/main/kotlin/com/intelligentbackpack/desktopdata/datasource/DesktopDomainRepositoryImpl.kt @@ -4,131 +4,103 @@ import com.intelligentbackpack.accessdomain.entities.User import com.intelligentbackpack.desktopdomain.entities.Book import com.intelligentbackpack.desktopdomain.entities.Desktop import com.intelligentbackpack.desktopdomain.entities.SchoolSupply +import com.intelligentbackpack.desktopdomain.exception.BackpackNotAssociatedException import com.intelligentbackpack.desktopdomain.repository.DesktopDomainRepository +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.withContext class DesktopDomainRepositoryImpl( private val desktopLocalDataSource: DesktopLocalDataSource, private val desktopRemoteDataSource: DesktopRemoteDataSource, ) : DesktopDomainRepository { - override suspend fun getDesktop(user: User, success: (Desktop) -> Unit, error: (Exception) -> Unit) { - try { + private fun updateBackpackContent(rfidInsert: Set) { + desktopLocalDataSource.putSchoolSuppliesInBackpack(rfidInsert) + val allSupplyRfid = desktopLocalDataSource.getAllSchoolSupplies().map { it.rfidCode }.toSet() + desktopLocalDataSource.takeSchoolSuppliesFromBackpack(allSupplyRfid - rfidInsert) + } + + override suspend fun downloadDesktop(user: User): Desktop = + withContext(Dispatchers.IO) { desktopRemoteDataSource.getDesktop(user) - .let { remoteDesktop -> + .also { remoteDesktop -> remoteDesktop.schoolSupplies.forEach { desktopLocalDataSource.addSchoolSupply(it) - }.let { - Desktop.create( - desktopLocalDataSource.getAllSchoolSupplies(), - desktopLocalDataSource.getSchoolSupplyInBackpack() - ) - }.also { desktop -> - desktop.backpack?.let { backpack -> - desktopLocalDataSource - .putSchoolSuppliesInBackpack( - desktop.schoolSuppliesInBackpack.map { it.rfidCode } - .toSet() - ) - desktopLocalDataSource - .takeSchoolSuppliesFromBackpack( - (desktop.schoolSupplies - desktop.schoolSuppliesInBackpack) - .map { it.rfidCode } - .toSet() - ) - desktopLocalDataSource.associateBackpack(backpack) + } + remoteDesktop.backpack?.let { backpack -> + updateBackpackContent(remoteDesktop.schoolSuppliesInBackpack.map { it.rfidCode }.toSet()) + desktopLocalDataSource.associateBackpack(backpack) + } ?: run { + updateBackpackContent(emptySet()) + desktopLocalDataSource.getBackpack()?.let { + desktopLocalDataSource.disassociateBackpack() } - success(desktop) } } - } catch (e: Exception) { - error(e) } - } - override suspend fun addSchoolSupply( - user: User, - schoolSupply: SchoolSupply, - success: (Set) -> Unit, - error: (Exception) -> Unit - ) { - try { + override suspend fun getDesktop(user: User): Desktop = + withContext(Dispatchers.IO) { + Desktop.create( + desktopLocalDataSource.getAllSchoolSupplies(), + desktopLocalDataSource.getSchoolSupplyInBackpack(), + desktopLocalDataSource.getBackpack() + ) + } + + + override suspend fun addSchoolSupply(user: User, schoolSupply: SchoolSupply) = + withContext(Dispatchers.IO) { desktopLocalDataSource.addSchoolSupply(schoolSupply) desktopRemoteDataSource.addSchoolSupply(user, schoolSupply) - success(desktopLocalDataSource.getAllSchoolSupplies()) - } catch (e: Exception) { - error(e) + desktopLocalDataSource.getAllSchoolSupplies() } - } - override suspend fun getBook(isbn: String, success: (Book?) -> Unit, error: (Exception) -> Unit) { - try { - desktopLocalDataSource - .getBook(isbn) - ?.let { success(it) } - ?: run { - success( - desktopRemoteDataSource.getBook(isbn) - ?.also { remoteBook -> - desktopLocalDataSource - .addBook(remoteBook) - }) - } - } catch (e: Exception) { - error(e) + override suspend fun getBook(isbn: String): Book? = + withContext(Dispatchers.IO) { + desktopLocalDataSource.getBook(isbn) + ?: desktopRemoteDataSource.getBook(isbn) + ?.also { remoteBook -> + desktopLocalDataSource.addBook(remoteBook) + } } - } - override suspend fun logoutDesktop(user: User, success: () -> Unit, error: (Exception) -> Unit) { - try { + override suspend fun logoutDesktop(user: User) = + withContext(Dispatchers.IO) { desktopLocalDataSource.deleteDesktop() - success() - } catch (e: Exception) { - error(e) } - } - - override fun subscribeToBackpack(user: User): Flow> { - val backpack = desktopLocalDataSource.getBackpack() - return desktopRemoteDataSource.subscribeToBackpackChanges(user, backpack).map { it.getOrDefault(setOf()) } - } - - override suspend fun putSchoolSuppliesInBackpack( - rfid: Set, - ) { - desktopLocalDataSource.putSchoolSuppliesInBackpack(rfid) - } - override suspend fun takeSchoolSuppliesFromBackpack( - rfid: Set, - ) { - desktopLocalDataSource.takeSchoolSuppliesFromBackpack(rfid) - } + override suspend fun subscribeToBackpack(user: User): Flow> = + withContext(Dispatchers.IO) { + desktopLocalDataSource.getBackpack()?.let { backpack -> + desktopRemoteDataSource.subscribeToBackpackChanges(user, backpack) + .flowOn(Dispatchers.IO) + .map { it.getOrDefault(setOf()) } + .map { rfidCodes -> + val taken = desktopLocalDataSource + .getSchoolSupplyInBackpack().map { it.rfidCode }.toSet() - rfidCodes + desktopLocalDataSource.putSchoolSuppliesInBackpack(rfidCodes) + desktopLocalDataSource.takeSchoolSuppliesFromBackpack(taken) + rfidCodes + } + } ?: throw BackpackNotAssociatedException() + } - override suspend fun associateBackpack(user: User, hash: String, success: (String) -> Unit, error: (Exception) -> Unit) { - try { + override suspend fun associateBackpack(user: User, hash: String) = + withContext(Dispatchers.IO) { val newHash = desktopRemoteDataSource.associateBackpack(user, hash) desktopLocalDataSource.associateBackpack(newHash) - success(newHash) - } catch (e: Exception) { - error(e) + newHash } - } - override suspend fun disassociateBackpack( - user: User, - hash: String, - success: (String) -> Unit, - error: (Exception) -> Unit - ) { - try { + override suspend fun disassociateBackpack(user: User, hash: String) = + withContext(Dispatchers.IO) { desktopRemoteDataSource.disassociateBackpack(user, hash) desktopLocalDataSource.disassociateBackpack() desktopLocalDataSource.removeAllSchoolSuppliesFromBackpack() - success(hash) - } catch (e: Exception) { - error(e) + hash } - } } diff --git a/desktopDomain/src/main/kotlin/com/intelligentbackpack/desktopdomain/repository/DesktopDomainRepository.kt b/desktopDomain/src/main/kotlin/com/intelligentbackpack/desktopdomain/repository/DesktopDomainRepository.kt index b600b934..28329649 100644 --- a/desktopDomain/src/main/kotlin/com/intelligentbackpack/desktopdomain/repository/DesktopDomainRepository.kt +++ b/desktopDomain/src/main/kotlin/com/intelligentbackpack/desktopdomain/repository/DesktopDomainRepository.kt @@ -11,83 +11,73 @@ import kotlinx.coroutines.flow.Flow */ interface DesktopDomainRepository { + /** + * Downloads the desktop. + * + * @param user the user that wants the desktop + * @return the desktop + */ + + suspend fun downloadDesktop(user: User): Desktop + /** * Gets the desktop. * - * @param success The success callback. - * @param error The error callback. + * @param user the user that wants the desktop + * @return the desktop */ - suspend fun getDesktop(user: User, success: (Desktop) -> Unit, error: (Exception) -> Unit) + suspend fun getDesktop(user: User): Desktop /** * Adds a school supply to the desktop. * + * @param user The user that adds the school supply. * @param schoolSupply The school supply to add. - * @param success The success callback with all the school supplies. - * @param error The error callback. + * @return The set of school supplies. */ suspend fun addSchoolSupply( user: User, schoolSupply: SchoolSupply, - success: (Set) -> Unit, - error: (Exception) -> Unit - ) + ): Set /** * Gets a book by its ISBN. * * @param isbn The ISBN of the book. - * @param success The success callback with the book if it exists. - * @param error The error callback. + * @return The book or null if it doesn't exist. */ - suspend fun getBook(isbn: String, success: (Book?) -> Unit, error: (Exception) -> Unit) + suspend fun getBook(isbn: String): Book? /** * Deletes the desktop and all its school supplies. * - * @param success The success callback. - * @param error The error callback. + * @param user The user that logout the desktop. */ - suspend fun logoutDesktop(user: User, success: () -> Unit, error: (Exception) -> Unit) + suspend fun logoutDesktop(user: User) /** * Subscribes to the backpack. * * @param user the user that subscribe to the backpack + * @return a [Flow] of [Set] of [String] representing the rfid of the school supplies in the backpack */ - fun subscribeToBackpack(user: User): Flow> - - /** - * Put a set of school supplies in the backpack - * - * @param rfid the set of rfid of school supplies to put in the backpack - */ - suspend fun putSchoolSuppliesInBackpack(rfid: Set) - - /** - * Take a set of school supplies from the backpack - * - * @param rfid the set of rfid of school supplies to take from the backpack - */ - suspend fun takeSchoolSuppliesFromBackpack(rfid: Set) + suspend fun subscribeToBackpack(user: User): Flow> /** * Associate the backpack to the desktop * * @param user the user that connect the backpack * @param hash the hash of the backpack to associate - * @param success The success callback with the backpack hash. - * @param error The error callback. + * @return the hash of the backpack */ - suspend fun associateBackpack(user: User, hash: String, success: (String) -> Unit, error: (Exception) -> Unit) + suspend fun associateBackpack(user: User, hash: String): String /** * Disassociate the backpack from the desktop * * @param user the user that disconnect the backpack * @param hash the hash of the backpack to disassociate - * @param success The success callback with the backpack hash. - * @param error The error callback. + * @return the hash of the backpack */ - suspend fun disassociateBackpack(user: User, hash: String, success: (String) -> Unit, error: (Exception) -> Unit) + suspend fun disassociateBackpack(user: User, hash: String): String } diff --git a/desktopDomain/src/main/kotlin/com/intelligentbackpack/desktopdomain/usecase/DesktopUseCase.kt b/desktopDomain/src/main/kotlin/com/intelligentbackpack/desktopdomain/usecase/DesktopUseCase.kt index 8ee4d43f..cb580e74 100644 --- a/desktopDomain/src/main/kotlin/com/intelligentbackpack/desktopdomain/usecase/DesktopUseCase.kt +++ b/desktopDomain/src/main/kotlin/com/intelligentbackpack/desktopdomain/usecase/DesktopUseCase.kt @@ -1,12 +1,9 @@ package com.intelligentbackpack.desktopdomain.usecase -import com.intelligentbackpack.accessdomain.entities.User import com.intelligentbackpack.accessdomain.usecase.AccessUseCase import com.intelligentbackpack.desktopdomain.entities.Book -import com.intelligentbackpack.desktopdomain.entities.BookCopy import com.intelligentbackpack.desktopdomain.entities.Desktop import com.intelligentbackpack.desktopdomain.entities.SchoolSupply -import com.intelligentbackpack.desktopdomain.entities.SchoolSupplyTypes import com.intelligentbackpack.desktopdomain.exception.BackpackNotAssociatedException import com.intelligentbackpack.desktopdomain.repository.DesktopDomainRepository import kotlinx.coroutines.flow.Flow @@ -19,26 +16,12 @@ import kotlinx.coroutines.runBlocking */ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repository: DesktopDomainRepository) { - private var desktop: Desktop? = null - - /** - * Gets the desktop. - * - * @param success the success callback with the user and the desktop. - * @param error the error callback. - */ - private suspend fun getDesktop(success: (User, Desktop) -> Unit, error: (Exception) -> Unit) { + suspend fun downloadDesktop(success: (Desktop) -> Unit, error: (Exception) -> Unit) { accessUseCase.automaticLogin({ user -> - desktop?.let { - success(user, it) - } ?: runBlocking { - repository.getDesktop( - user, - { - desktop = it - success(user, it) - }, error - ) + try { + success(repository.downloadDesktop(user)) + } catch (e: Exception) { + error(e) } }, error) } @@ -50,7 +33,13 @@ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repos * @param error the error callback. */ suspend fun getDesktop(success: (Desktop) -> Unit, error: (Exception) -> Unit) { - getDesktop({ _, desktop -> success(desktop) }, error) + accessUseCase.automaticLogin({ user -> + try { + success(repository.getDesktop(user)) + } catch (e: Exception) { + error(e) + } + }, error) } /** @@ -65,19 +54,18 @@ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repos success: (Desktop) -> Unit, error: (Exception) -> Unit ) { - val addSupply: (User, Desktop) -> Unit = { user, it -> - try { - it.addSchoolSupply(schoolSupply) - runBlocking { - repository.addSchoolSupply(user, schoolSupply, { - success(desktop!!) - }, error) + accessUseCase.automaticLogin({ user -> + runBlocking { + try { + val desktop = repository.getDesktop(user) + desktop.addSchoolSupply(schoolSupply) + repository.addSchoolSupply(user, schoolSupply) + success(desktop) + } catch (e: Exception) { + error(e) } - } catch (e: Exception) { - error(e) } - } - getDesktop(addSupply, error) + }, error) } /** @@ -88,18 +76,11 @@ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repos * @param error The error callback. */ suspend fun getBook(isbn: String, success: (Book?) -> Unit, error: (Exception) -> Unit) { - val getBook: (Desktop) -> Unit = { - try { - val book = it.schoolSupplies - .filter { supply -> supply.type == SchoolSupplyTypes.BOOK } - .map { supply -> (supply as BookCopy).book } - .firstOrNull { book -> book.isbn == isbn } - book?.let(success) ?: runBlocking { repository.getBook(isbn, success, error) } - } catch (e: Exception) { - error(e) - } + try { + success(repository.getBook(isbn)) + } catch (e: Exception) { + error(e) } - getDesktop(getBook, error) } /** @@ -110,14 +91,15 @@ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repos * @param error The error callback. */ suspend fun getSchoolSupply(rfid: String, success: (SchoolSupply?) -> Unit, error: (Exception) -> Unit) { - val getSchoolSupply: (Desktop) -> Unit = { - try { - success(it.schoolSupplies.firstOrNull { supply -> supply.rfidCode == rfid }) - } catch (e: Exception) { - error(e) + accessUseCase.automaticLogin({ user -> + runBlocking { + try { + success(repository.getDesktop(user).schoolSupplies.firstOrNull { it.rfidCode == rfid }) + } catch (e: Exception) { + error(e) + } } - } - getDesktop(getSchoolSupply, error) + }, error) } /** @@ -128,19 +110,16 @@ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repos * @param error The error callback. */ suspend fun associateBackpack(hash: String, success: (Desktop) -> Unit, error: (Exception) -> Unit) { - val connectBackpack: (User, Desktop) -> Unit = { user, internalBackpack -> + accessUseCase.automaticLogin({ user -> try { - runBlocking { - repository.associateBackpack(user, hash, { - internalBackpack.associateBackpack(it) - success(internalBackpack) - }, error) - } + val returnedHash = repository.associateBackpack(user, hash) + val desktop = repository.getDesktop(user) + desktop.associateBackpack(returnedHash) + success(desktop) } catch (e: Exception) { error(e) } - } - getDesktop(connectBackpack, error) + }, error) } /** @@ -149,60 +128,23 @@ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repos * The [Flow] emits the [Set] of [SchoolSupply] when the backpack changes. * The repository emits the changes of the backpack when the desktop changes. * The desktop changes when the backpack changes. - * The repository receives the changes of the backpack with method [DesktopDomainRepository.putSchoolSuppliesInBackpack] - * and [DesktopDomainRepository.takeSchoolSuppliesFromBackpack]. * * @param success The success callback with a [Flow] of the school supplies in backpack. * @param error The error callback. */ suspend fun subscribeToBackpack(success: (Flow>) -> Unit, error: (Exception) -> Unit) { - val putSchoolSuppliesInBackpack = { backpack: Set -> - val newAdd = backpack - desktop?.schoolSuppliesInBackpack.orEmpty() - desktop?.putSchoolSuppliesInBackpack(newAdd) - ?.also { - runBlocking { - repository.putSchoolSuppliesInBackpack( - newAdd.map { supply -> supply.rfidCode }.toSet() - ) - } - } - } - - val takeSchoolSuppliesFromBackpack = { backpack: Set -> - val newRemove = desktop?.schoolSuppliesInBackpack.orEmpty() - backpack - desktop?.takeSchoolSuppliesFromBackpack(newRemove) - ?.also { - runBlocking { - repository.takeSchoolSuppliesFromBackpack( - newRemove.map { supply -> supply.rfidCode }.toSet() - ) + accessUseCase.automaticLogin({ user -> + try { + val flow = repository.subscribeToBackpack(user) + .map { + repository.getDesktop(user).schoolSupplies.filter { supply -> + it.contains(supply.rfidCode) + }.toSet() } - } - } - - val updateBackpack = { backpack: Set -> - putSchoolSuppliesInBackpack(backpack) - takeSchoolSuppliesFromBackpack(backpack) - } - - val getSupplyInBackpack = { backpackRfid: Set -> - desktop?.schoolSupplies - ?.filter { supply -> backpackRfid.contains(supply.rfidCode) } - ?.toSet() ?: emptySet() - } - - getDesktop({ user, _ -> - runBlocking { - success( - repository.subscribeToBackpack(user) - .conflate() - .map { backpackRfid -> - getSupplyInBackpack(backpackRfid) - .also { backpack -> - updateBackpack(backpack) - } - } - ) + .conflate() + success(flow) + } catch (e: Exception) { + error(e) } }, error) } @@ -214,10 +156,12 @@ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repos * @param error The error callback. */ suspend fun logoutDesktop(success: () -> Unit, error: (Exception) -> Unit) { - desktop = null accessUseCase.automaticLogin({ user -> - runBlocking { - repository.logoutDesktop(user, success, error) + try { + repository.logoutDesktop(user) + success() + } catch (e: Exception) { + error(e) } }, error) } @@ -229,27 +173,24 @@ class DesktopUseCase(private val accessUseCase: AccessUseCase, private val repos * @param success The success callback. * @param error The error callback. */ - suspend fun disassociateBackpack(hash: String, success: () -> Unit, error: (Exception) -> Unit) { - val disassociateBackpack: (User, Desktop) -> Unit = { user, internalDesktop -> - internalDesktop.backpack?.let { - if (it == hash) { + suspend fun disassociateBackpack(hash: String, success: (Desktop) -> Unit, error: (Exception) -> Unit) { + accessUseCase.automaticLogin({ user -> + val desktop = repository.getDesktop(user) + if (desktop.isBackpackAssociated) { + if (desktop.backpack == hash) { try { - runBlocking { - repository.disassociateBackpack(user, hash, { deleted -> - internalDesktop.disassociateBackpack(deleted) - success() - }, error) - } + repository.disassociateBackpack(user, hash) + desktop.disassociateBackpack(hash) + success(desktop) } catch (e: Exception) { error(e) } } else { error(BackpackNotAssociatedException()) } - } ?: run { + } else { error(BackpackNotAssociatedException()) } - } - getDesktop(disassociateBackpack, error) + }, error) } }