Skip to content

Commit

Permalink
Return updated/created tasks with remote data source
Browse files Browse the repository at this point in the history
  • Loading branch information
kamerok committed Sep 5, 2024
1 parent 20cdd7b commit 9ab533f
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface RemoteDataSource {
suspend fun isConnected(): Boolean
suspend fun getAllTasks(): List<Task>
suspend fun isTaskKnownOnRemote(id: String): Boolean
suspend fun updateTask(delta: TaskDelta)
suspend fun createTask(delta: TaskDelta)
suspend fun updateTask(delta: TaskDelta): Task
suspend fun createTask(delta: TaskDelta): Task
suspend fun disconnect()
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ class Synchronizer(
) {

suspend fun sync() {
// get remote data
// sync change and apply it to the remote data
// also sync waiting if needed (this renders flushing new delta unneeded)
// flush remote data to local
uploadChanges()
refreshLocalData()
}
Expand Down
67 changes: 59 additions & 8 deletions core/domain/src/test/java/easydone/core/domain/SynchronizerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package easydone.core.domain

import assertk.assertThat
import assertk.assertions.containsOnly
import easydone.core.domain.model.Markers
import easydone.core.domain.model.Task
import easydone.core.domain.model.TaskDelta
import easydone.core.domain.model.TaskTemplate
Expand All @@ -14,7 +15,7 @@ import kotlin.reflect.KClass
class SynchronizerTest {

@Test
fun `Test saving task`() = runTest {
fun `Delta for unknown task should create a card on remote`() = runTest {
val delta = taskDelta()
val remoteDataSource = remoteDataSource(isTaskKnown = false)
val localDataSource = localDataSource(listOf(delta))
Expand All @@ -26,7 +27,13 @@ class SynchronizerTest {
}

@Test
fun `Test updating task`() = runTest {
fun `Created card should be saved locally`() {
//ignore original server card with this id

}

@Test
fun `Delta for unknown task should update create a card on remote`() = runTest {
val delta = taskDelta()
val remoteDataSource = remoteDataSource(isTaskKnown = true)
val localDataSource = localDataSource(listOf(delta))
Expand All @@ -37,6 +44,38 @@ class SynchronizerTest {
assertThat(remoteDataSource.getUpdatedDeltas()).containsOnly(delta)
}

@Test
fun `Updated card should be saved locally`() {
//ignore original server card with this id

}

@Test
fun `Task from remote source should be saved locally`() = runTest {
val task = task("remote_task")
val remoteDataSource = remoteDataSource(remoteTasks = listOf(task))
val localDataSource = localDataSource()
val synchronizer = Synchronizer(remoteDataSource, localDataSource)

synchronizer.sync()

assertThat(localDataSource.refreshedTasks()).containsOnly(task)
}

@Test
fun `Waiting task with reached date should be updated`() {

}

private fun task(id: String) = Task(
id = id,
type = Task.Type.Inbox,
title = "Task $id",
description = "",
markers = Markers(isUrgent = false, isImportant = false),
isDone = false
)

private fun taskDelta() = TaskDelta(
id = 1,
taskId = "taskId",
Expand All @@ -47,22 +86,28 @@ class SynchronizerTest {
isDone = null
)

private fun remoteDataSource(isTaskKnown: Boolean) = object : RemoteDataSource {
private fun remoteDataSource(
isTaskKnown: Boolean = false,
remoteTasks: List<Task> = emptyList(),
returnedTask: Task = task("returned_task")
) = object : RemoteDataSource {
private val createdDeltas: MutableList<TaskDelta> = mutableListOf()
private val updatedDeltas: MutableList<TaskDelta> = mutableListOf()

override suspend fun isConnected(): Boolean = true

override suspend fun getAllTasks(): List<Task> = emptyList()
override suspend fun getAllTasks(): List<Task> = remoteTasks

override suspend fun isTaskKnownOnRemote(id: String): Boolean = isTaskKnown

override suspend fun updateTask(delta: TaskDelta) {
override suspend fun updateTask(delta: TaskDelta): Task {
updatedDeltas.add(delta)
return returnedTask
}

override suspend fun createTask(delta: TaskDelta) {
override suspend fun createTask(delta: TaskDelta): Task {
createdDeltas.add(delta)
return returnedTask
}

override suspend fun disconnect() {}
Expand All @@ -74,6 +119,8 @@ class SynchronizerTest {

private fun localDataSource(changes: List<TaskDelta> = emptyList()) =
object : LocalDataSource {
private var refreshedTasks: List<Task> = emptyList()

override suspend fun getChanges(): List<TaskDelta> = changes

override fun observeChangesCount(): Flow<Long> = emptyFlow()
Expand All @@ -85,18 +132,22 @@ class SynchronizerTest {
override fun observeTask(id: String): Flow<Task> = emptyFlow()

override suspend fun getTask(id: String): Task {
TODO("Not yet implemented")
throw NotImplementedError()
}

override suspend fun createTask(taskTemplate: TaskTemplate) {}

override suspend fun updateTask(task: Task) {}

override suspend fun refreshData(tasks: List<Task>, updatedTasks: List<Task>) {}
override suspend fun refreshData(tasks: List<Task>, updatedTasks: List<Task>) {
refreshedTasks = tasks
}

override suspend fun deleteChange(id: Long) {
}

fun refreshedTasks(): List<Task> = refreshedTasks

override suspend fun removeAllData() {}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ class SandboxRemoteDataSource : RemoteDataSource {

override suspend fun isTaskKnownOnRemote(id: String): Boolean = false

override suspend fun updateTask(delta: TaskDelta) {}
override suspend fun updateTask(delta: TaskDelta): Task {
throw NotImplementedError()
}

override suspend fun createTask(delta: TaskDelta) {}
override suspend fun createTask(delta: TaskDelta): Task {
throw NotImplementedError()
}

override suspend fun disconnect() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,47 +31,25 @@ class TrelloRemoteDataSource(
val token = authInfoHolder.getToken()!!
val board = api.boardData(boardId, apiKey, token)
rememberDataAnchors(board)
return@withContext board.cards
board.cards
.filter {
it.idList == authInfoHolder.getTodoListId() ||
it.idList == authInfoHolder.getInboxListId() ||
it.idList == authInfoHolder.getWaitingListId() ||
it.idList == authInfoHolder.getProjectsListId() ||
it.idList == authInfoHolder.getMaybeListId()
}
.map { card ->
val localId = idMappings.getString(card.id, UUID.randomUUID().toString())
if (!idMappings.contains(card.id)) {
idMappings.putString(localId, card.id)
idMappings.putString(card.id, localId)
}

val type = when (card.idList) {
authInfoHolder.getInboxListId() -> Task.Type.Inbox
authInfoHolder.getWaitingListId() -> Task.Type.Waiting(
//todo: handle waiting items with no date
requireNotNull(card.due)
.let { LocalDate.parse(it, DateTimeFormatter.ISO_DATE_TIME) }
)

authInfoHolder.getProjectsListId() -> Task.Type.Project
authInfoHolder.getMaybeListId() -> Task.Type.Maybe
else -> Task.Type.ToDo
}
val isUrgent = card.idLabels.contains(authInfoHolder.getUrgentLabelId()!!)
val isImportant = card.idLabels.contains(authInfoHolder.getImportantLabelId()!!)
card.toTask(localId, type, isUrgent, isImportant)
}
.map { card -> card.toTask() }
}

override suspend fun isTaskKnownOnRemote(id: String): Boolean = idMappings.contains(id)

override suspend fun updateTask(delta: TaskDelta) {
withContext(Dispatchers.IO) {
override suspend fun updateTask(delta: TaskDelta): Task {
return withContext(Dispatchers.IO) {
val serverId: String = requireNotNull(
idMappings.getString(delta.taskId)
) { "Try to update task with $delta but server id was not found" }
api.editCard(
val card = api.editCard(
serverId,
apiKey,
authInfoHolder.getToken()!!,
Expand All @@ -82,11 +60,12 @@ class TrelloRemoteDataSource(
listId = delta.type?.let { getListId(it) },
idLabels = delta.convertMarkersToLabels()
)
card.toTask()
}
}

override suspend fun createTask(delta: TaskDelta) {
withContext(Dispatchers.IO) {
override suspend fun createTask(delta: TaskDelta): Task {
return withContext(Dispatchers.IO) {
val card = api.postCard(
listId = getListId(delta.type!!),
name = delta.title!!,
Expand All @@ -98,7 +77,42 @@ class TrelloRemoteDataSource(
)
idMappings.putString(delta.taskId, card.id)
idMappings.putString(card.id, delta.taskId)
card.toTask()
}
}

private suspend fun Card.toTask(): Task {
val localId = idMappings.getString(id, UUID.randomUUID().toString())
if (!idMappings.contains(id)) {
idMappings.putString(localId, id)
idMappings.putString(id, localId)
}

val type = when (idList) {
authInfoHolder.getInboxListId() -> Task.Type.Inbox
authInfoHolder.getWaitingListId() -> Task.Type.Waiting(
//todo: handle waiting items with no date
requireNotNull(due)
.let { LocalDate.parse(it, DateTimeFormatter.ISO_DATE_TIME) }
)

authInfoHolder.getProjectsListId() -> Task.Type.Project
authInfoHolder.getMaybeListId() -> Task.Type.Maybe
else -> Task.Type.ToDo
}
val isUrgent = idLabels.contains(authInfoHolder.getUrgentLabelId()!!)
val isImportant = idLabels.contains(authInfoHolder.getImportantLabelId()!!)
return Task(
id = localId,
type = type,
title = name,
description = desc,
markers = Markers(
isUrgent = isUrgent,
isImportant = isImportant
),
isDone = false
)
}

private suspend fun TaskDelta.convertMarkersToLabels(): String? {
Expand Down Expand Up @@ -163,23 +177,4 @@ class TrelloRemoteDataSource(
}
}

private fun Card.toTask(
id: String,
type: Task.Type,
isUrgent: Boolean,
isImportant: Boolean
): Task {
return Task(
id = id,
type = type,
title = name,
description = desc,
markers = Markers(
isUrgent = isUrgent,
isImportant = isImportant
),
isDone = false
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact
import easydone.service.trello.api.model.Card
import easydone.service.trello.api.model.NestedBoard
import easydone.service.trello.api.model.NestedBoards
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
Expand Down Expand Up @@ -60,7 +59,6 @@ interface TrelloApi {
ignoreUnknownKeys = true
}

@OptIn(ExperimentalSerializationApi::class)
fun build(debugInterceptor: Interceptor?): TrelloApi = Retrofit.Builder()
.baseUrl("https://trello.com/1/")
.client(
Expand Down

0 comments on commit 9ab533f

Please sign in to comment.