-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
create a dirty favorite tools sync task
- Loading branch information
Showing
4 changed files
with
206 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
library/sync/src/main/kotlin/org/cru/godtools/sync/task/UserFavoriteToolsSyncTasks.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package org.cru.godtools.sync.task | ||
|
||
import javax.inject.Inject | ||
import javax.inject.Singleton | ||
import kotlinx.coroutines.coroutineScope | ||
import kotlinx.coroutines.launch | ||
import kotlinx.coroutines.sync.Mutex | ||
import kotlinx.coroutines.sync.withLock | ||
import org.ccci.gto.android.common.jsonapi.JsonApiConverter | ||
import org.ccci.gto.android.common.jsonapi.retrofit2.JsonApiParams | ||
import org.ccci.gto.android.common.jsonapi.retrofit2.model.JsonApiRetrofitObject | ||
import org.cru.godtools.account.GodToolsAccountManager | ||
import org.cru.godtools.api.UserApi | ||
import org.cru.godtools.api.UserFavoriteToolsApi | ||
import org.cru.godtools.db.repository.ToolsRepository | ||
import org.cru.godtools.db.repository.UserRepository | ||
import org.cru.godtools.model.Tool | ||
import org.cru.godtools.model.User | ||
import org.cru.godtools.sync.repository.SyncRepository | ||
|
||
@Singleton | ||
internal class UserFavoriteToolsSyncTasks @Inject constructor( | ||
private val accountManager: GodToolsAccountManager, | ||
private val favoritesApi: UserFavoriteToolsApi, | ||
private val syncRepository: SyncRepository, | ||
private val toolsRepository: ToolsRepository, | ||
private val userApi: UserApi, | ||
private val userRepository: UserRepository, | ||
) : BaseSyncTasks() { | ||
private val favoritesUpdateMutex = Mutex() | ||
|
||
suspend fun syncDirtyFavoriteTools(): Boolean = coroutineScope { | ||
favoritesUpdateMutex.withLock { | ||
if (!accountManager.isAuthenticated()) return@coroutineScope true | ||
val userId = accountManager.userId().orEmpty() | ||
|
||
val user = userRepository.findUser(userId)?.takeIf { it.isInitialFavoriteToolsSynced } | ||
?: userApi.getUser().takeIf { it.isSuccessful } | ||
?.body()?.dataSingle | ||
?.also { syncRepository.storeUser(it) } | ||
?: return@coroutineScope false | ||
|
||
val favoritesToAdd = toolsRepository.getResources() | ||
.filter { | ||
(it.isFieldChanged(Tool.ATTR_IS_FAVORITE) || !user.isInitialFavoriteToolsSynced) && it.isFavorite | ||
} | ||
|
||
val params = JsonApiParams().fields(Tool.JSONAPI_TYPE, *Tool.JSONAPI_FIELDS) | ||
if (favoritesToAdd.isNotEmpty()) { | ||
favoritesApi.addFavoriteTools(params, favoritesToAdd).takeIf { it.isSuccessful } | ||
?.body()?.data | ||
?.also { syncRepository.storeFavoriteTools(it) } | ||
?: return@coroutineScope false | ||
|
||
if (!user.isInitialFavoriteToolsSynced) { | ||
launch { | ||
val update = JsonApiRetrofitObject.single(User(userId, isInitialFavoriteToolsSynced = true)) | ||
.apply { | ||
options = JsonApiConverter.Options.Builder() | ||
.fields(User.JSONAPI_TYPE, User.JSON_INITIAL_FAVORITE_TOOLS_SYNCED) | ||
.build() | ||
} | ||
|
||
userApi.updateUser(update).takeIf { it.isSuccessful } | ||
?.body()?.dataSingle | ||
?.also { syncRepository.storeUser(it) } | ||
?: return@launch | ||
} | ||
} | ||
} | ||
|
||
val favoritesToRemove = toolsRepository.getResources() | ||
.filter { it.isFieldChanged(Tool.ATTR_IS_FAVORITE) && !it.isFavorite } | ||
|
||
if (favoritesToRemove.isNotEmpty()) { | ||
favoritesApi.removeFavoriteTools(params, favoritesToRemove).takeIf { it.isSuccessful } | ||
?.body()?.data | ||
?.also { syncRepository.storeFavoriteTools(it) } | ||
?: return@coroutineScope false | ||
} | ||
|
||
true | ||
} | ||
} | ||
} |
112 changes: 112 additions & 0 deletions
112
library/sync/src/test/kotlin/org/cru/godtools/sync/task/UserFavoriteToolsSyncTasksTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package org.cru.godtools.sync.task | ||
|
||
import io.mockk.Called | ||
import io.mockk.Runs | ||
import io.mockk.coEvery | ||
import io.mockk.coVerifySequence | ||
import io.mockk.just | ||
import io.mockk.mockk | ||
import java.util.UUID | ||
import kotlin.test.Test | ||
import kotlin.test.assertFalse | ||
import kotlin.test.assertTrue | ||
import kotlinx.coroutines.test.runTest | ||
import org.ccci.gto.android.common.jsonapi.model.JsonApiObject | ||
import org.cru.godtools.account.GodToolsAccountManager | ||
import org.cru.godtools.api.UserApi | ||
import org.cru.godtools.api.UserFavoriteToolsApi | ||
import org.cru.godtools.db.repository.ToolsRepository | ||
import org.cru.godtools.db.repository.UserRepository | ||
import org.cru.godtools.model.User | ||
import org.cru.godtools.sync.repository.SyncRepository | ||
import retrofit2.Response | ||
|
||
class UserFavoriteToolsSyncTasksTest { | ||
private val userId = UUID.randomUUID().toString() | ||
|
||
private val accountManager: GodToolsAccountManager = mockk { | ||
coEvery { isAuthenticated() } returns true | ||
coEvery { userId() } returns userId | ||
} | ||
private val favoritesApi: UserFavoriteToolsApi = mockk() | ||
private val syncRepository: SyncRepository = mockk { | ||
coEvery { storeUser(any(), any()) } just Runs | ||
} | ||
private val toolsRepository: ToolsRepository = mockk { | ||
coEvery { getResources() } returns emptyList() | ||
} | ||
private val userApi: UserApi = mockk { | ||
coEvery { getUser(any()) } | ||
.returns(Response.success(JsonApiObject.single(User(userId, isInitialFavoriteToolsSynced = true)))) | ||
} | ||
private val userRepository: UserRepository = mockk { | ||
coEvery { findUser(userId) } returns User(userId, isInitialFavoriteToolsSynced = true) | ||
} | ||
|
||
private val tasks = UserFavoriteToolsSyncTasks( | ||
accountManager = accountManager, | ||
favoritesApi = favoritesApi, | ||
syncRepository = syncRepository, | ||
toolsRepository = toolsRepository, | ||
userApi = userApi, | ||
userRepository = userRepository, | ||
) | ||
|
||
// region syncDirtyFavoriteTools() | ||
@Test | ||
fun `syncDirtyFavoriteTools() - not authenticated`() = runTest { | ||
coEvery { accountManager.isAuthenticated() } returns false | ||
|
||
assertTrue(tasks.syncDirtyFavoriteTools()) | ||
coVerifySequence { | ||
accountManager.isAuthenticated() | ||
|
||
userRepository wasNot Called | ||
userApi wasNot Called | ||
syncRepository wasNot Called | ||
toolsRepository wasNot Called | ||
favoritesApi wasNot Called | ||
} | ||
} | ||
|
||
@Test | ||
fun `syncDirtyFavoriteTools() - user not found`() = runTest { | ||
coEvery { userRepository.findUser(userId) } returns null | ||
coEvery { userApi.getUser(any()) } returns Response.success(JsonApiObject.of()) | ||
|
||
assertFalse(tasks.syncDirtyFavoriteTools()) | ||
coVerifySequence { | ||
userRepository.findUser(userId) | ||
userApi.getUser(any()) | ||
|
||
syncRepository wasNot Called | ||
toolsRepository wasNot Called | ||
favoritesApi wasNot Called | ||
} | ||
} | ||
|
||
@Test | ||
fun `syncDirtyFavoriteTools() - user resolved from API - user not found`() = runTest { | ||
coEvery { userRepository.findUser(userId) } returns null | ||
|
||
assertTrue(tasks.syncDirtyFavoriteTools()) | ||
coVerifySequence { | ||
userRepository.findUser(userId) | ||
userApi.getUser(any()) | ||
syncRepository.storeUser(any(), any()) | ||
} | ||
} | ||
|
||
@Test | ||
fun `syncDirtyFavoriteTools() - user resolved from API - user hasn't synced initial favorites`() = runTest { | ||
coEvery { userRepository.findUser(userId) } returns User(userId, isInitialFavoriteToolsSynced = false) | ||
|
||
assertTrue(tasks.syncDirtyFavoriteTools()) | ||
coVerifySequence { | ||
userRepository.findUser(userId) | ||
userApi.getUser(any()) | ||
syncRepository.storeUser(any(), any()) | ||
} | ||
} | ||
// endregion syncDirtyFavoriteTools() | ||
} |