feat: add card detail repository with session-aware operations
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
package space.hackenslacker.kanbn4droid.app.carddetail
|
||||
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.time.LocalDate
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardsApiResult
|
||||
|
||||
class CardDetailRepository(
|
||||
private val sessionStore: SessionStore,
|
||||
private val apiKeyStore: ApiKeyStore,
|
||||
private val apiClient: KanbnApiClient,
|
||||
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
const val MISSING_SESSION_MESSAGE = "Missing session. Please sign in again."
|
||||
private const val SESSION_EXPIRED_MESSAGE = "Session expired. Please sign in again."
|
||||
}
|
||||
|
||||
sealed interface Result<out T> {
|
||||
data class Success<T>(val value: T) : Result<T>
|
||||
|
||||
sealed interface Failure : Result<Nothing> {
|
||||
val message: String
|
||||
|
||||
data class Generic(override val message: String) : Failure
|
||||
data class SessionExpired(override val message: String = SESSION_EXPIRED_MESSAGE) : Failure
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateTitle(cardId: String, title: String): Result<Unit> {
|
||||
val normalizedCardId = cardId.trim()
|
||||
if (normalizedCardId.isBlank()) {
|
||||
return Result.Failure.Generic("Card id is required")
|
||||
}
|
||||
|
||||
val normalizedTitle = title.trim()
|
||||
if (normalizedTitle.isBlank()) {
|
||||
return Result.Failure.Generic("Card title is required")
|
||||
}
|
||||
|
||||
val session = when (val sessionResult = session()) {
|
||||
is Result.Success -> sessionResult.value
|
||||
is Result.Failure -> return sessionResult
|
||||
}
|
||||
|
||||
val detail = when (
|
||||
val detailResult = apiClient.getCardDetail(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
cardId = normalizedCardId,
|
||||
)
|
||||
) {
|
||||
is BoardsApiResult.Success -> detailResult.value
|
||||
is BoardsApiResult.Failure -> return mapFailure(detailResult.message)
|
||||
}
|
||||
|
||||
return when (
|
||||
val updateResult = apiClient.updateCard(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
cardId = normalizedCardId,
|
||||
title = normalizedTitle,
|
||||
description = detail.description,
|
||||
dueDate = detail.dueDate,
|
||||
)
|
||||
) {
|
||||
is BoardsApiResult.Success -> Result.Success(Unit)
|
||||
is BoardsApiResult.Failure -> mapFailure(updateResult.message)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateDescription(cardId: String, description: String?): Result<Unit> {
|
||||
val normalizedCardId = cardId.trim()
|
||||
if (normalizedCardId.isBlank()) {
|
||||
return Result.Failure.Generic("Card id is required")
|
||||
}
|
||||
|
||||
val normalizedDescription = description?.trim()?.takeIf { it.isNotBlank() }
|
||||
|
||||
val session = when (val sessionResult = session()) {
|
||||
is Result.Success -> sessionResult.value
|
||||
is Result.Failure -> return sessionResult
|
||||
}
|
||||
|
||||
val detail = when (
|
||||
val detailResult = apiClient.getCardDetail(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
cardId = normalizedCardId,
|
||||
)
|
||||
) {
|
||||
is BoardsApiResult.Success -> detailResult.value
|
||||
is BoardsApiResult.Failure -> return mapFailure(detailResult.message)
|
||||
}
|
||||
|
||||
return when (
|
||||
val updateResult = apiClient.updateCard(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
cardId = normalizedCardId,
|
||||
title = detail.title,
|
||||
description = normalizedDescription ?: "",
|
||||
dueDate = detail.dueDate,
|
||||
)
|
||||
) {
|
||||
is BoardsApiResult.Success -> Result.Success(Unit)
|
||||
is BoardsApiResult.Failure -> mapFailure(updateResult.message)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun updateDueDate(cardId: String, dueDate: LocalDate?): Result<Unit> {
|
||||
val normalizedCardId = cardId.trim()
|
||||
if (normalizedCardId.isBlank()) {
|
||||
return Result.Failure.Generic("Card id is required")
|
||||
}
|
||||
|
||||
val session = when (val sessionResult = session()) {
|
||||
is Result.Success -> sessionResult.value
|
||||
is Result.Failure -> return sessionResult
|
||||
}
|
||||
|
||||
val detail = when (
|
||||
val detailResult = apiClient.getCardDetail(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
cardId = normalizedCardId,
|
||||
)
|
||||
) {
|
||||
is BoardsApiResult.Success -> detailResult.value
|
||||
is BoardsApiResult.Failure -> return mapFailure(detailResult.message)
|
||||
}
|
||||
|
||||
return when (
|
||||
val updateResult = apiClient.updateCard(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
cardId = normalizedCardId,
|
||||
title = detail.title,
|
||||
description = detail.description,
|
||||
dueDate = dueDate,
|
||||
)
|
||||
) {
|
||||
is BoardsApiResult.Success -> Result.Success(Unit)
|
||||
is BoardsApiResult.Failure -> mapFailure(updateResult.message)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun listActivities(cardId: String): Result<List<CardActivity>> {
|
||||
val normalizedCardId = cardId.trim()
|
||||
if (normalizedCardId.isBlank()) {
|
||||
return Result.Failure.Generic("Card id is required")
|
||||
}
|
||||
|
||||
val session = when (val sessionResult = session()) {
|
||||
is Result.Success -> sessionResult.value
|
||||
is Result.Failure -> return sessionResult
|
||||
}
|
||||
|
||||
return when (
|
||||
val activitiesResult = apiClient.listCardActivities(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
cardId = normalizedCardId,
|
||||
)
|
||||
) {
|
||||
is BoardsApiResult.Success -> Result.Success(
|
||||
activitiesResult.value
|
||||
.sortedByDescending { it.createdAtEpochMillis }
|
||||
.take(10),
|
||||
)
|
||||
|
||||
is BoardsApiResult.Failure -> mapFailure(activitiesResult.message)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun addComment(cardId: String, comment: String): Result<List<CardActivity>> {
|
||||
val normalizedCardId = cardId.trim()
|
||||
if (normalizedCardId.isBlank()) {
|
||||
return Result.Failure.Generic("Card id is required")
|
||||
}
|
||||
|
||||
val normalizedComment = comment.trim()
|
||||
if (normalizedComment.isBlank()) {
|
||||
return Result.Failure.Generic("Comment is required")
|
||||
}
|
||||
|
||||
val session = when (val sessionResult = session()) {
|
||||
is Result.Success -> sessionResult.value
|
||||
is Result.Failure -> return sessionResult
|
||||
}
|
||||
|
||||
when (
|
||||
val addCommentResult = apiClient.addCardComment(
|
||||
baseUrl = session.baseUrl,
|
||||
apiKey = session.apiKey,
|
||||
cardId = normalizedCardId,
|
||||
comment = normalizedComment,
|
||||
)
|
||||
) {
|
||||
is BoardsApiResult.Success -> Unit
|
||||
is BoardsApiResult.Failure -> return mapFailure(addCommentResult.message)
|
||||
}
|
||||
|
||||
return listActivities(normalizedCardId)
|
||||
}
|
||||
|
||||
private suspend fun session(): Result<SessionSnapshot> {
|
||||
val baseUrl = sessionStore.getBaseUrl()?.takeIf { it.isNotBlank() }
|
||||
?: return Result.Failure.SessionExpired(MISSING_SESSION_MESSAGE)
|
||||
val apiKey = withContext(ioDispatcher) {
|
||||
apiKeyStore.getApiKey(baseUrl)
|
||||
}.getOrNull()?.takeIf { it.isNotBlank() }
|
||||
?: return Result.Failure.SessionExpired(MISSING_SESSION_MESSAGE)
|
||||
|
||||
return Result.Success(SessionSnapshot(baseUrl = baseUrl, apiKey = apiKey))
|
||||
}
|
||||
|
||||
private fun mapFailure(message: String): Result.Failure {
|
||||
val normalizedMessage = message.trim().ifBlank { "Unknown error" }
|
||||
return if (isAuthFailure(normalizedMessage)) {
|
||||
Result.Failure.SessionExpired()
|
||||
} else {
|
||||
Result.Failure.Generic(normalizedMessage)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAuthFailure(message: String): Boolean {
|
||||
val lower = message.lowercase()
|
||||
return lower.contains("authentication failed") ||
|
||||
lower.contains("server error: 401") ||
|
||||
lower.contains("server error: 403") ||
|
||||
lower.contains(" 401") ||
|
||||
lower.contains(" 403")
|
||||
}
|
||||
|
||||
private data class SessionSnapshot(
|
||||
val baseUrl: String,
|
||||
val apiKey: String,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
package space.hackenslacker.kanbn4droid.app.carddetail
|
||||
|
||||
import java.time.LocalDate
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||
import space.hackenslacker.kanbn4droid.app.auth.AuthResult
|
||||
import space.hackenslacker.kanbn4droid.app.auth.KanbnApiClient
|
||||
import space.hackenslacker.kanbn4droid.app.auth.LabelDetail
|
||||
import space.hackenslacker.kanbn4droid.app.auth.SessionStore
|
||||
import space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetail
|
||||
import space.hackenslacker.kanbn4droid.app.boarddetail.CreatedEntityRef
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardSummary
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardTemplate
|
||||
import space.hackenslacker.kanbn4droid.app.boards.BoardsApiResult
|
||||
import space.hackenslacker.kanbn4droid.app.boards.WorkspaceSummary
|
||||
|
||||
class CardDetailRepositoryTest {
|
||||
|
||||
@Test
|
||||
fun missingSession_returnsSessionExpiredFailure() = runTest {
|
||||
val repository = createRepository(
|
||||
sessionStore = InMemorySessionStore(baseUrl = null),
|
||||
)
|
||||
|
||||
val result = repository.listActivities("card-1")
|
||||
|
||||
assertTrue(result is CardDetailRepository.Result.Failure.SessionExpired)
|
||||
assertEquals(
|
||||
CardDetailRepository.MISSING_SESSION_MESSAGE,
|
||||
(result as CardDetailRepository.Result.Failure.SessionExpired).message,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateDescription_blankMapsToNull_andPreservesFailureMessage() = runTest {
|
||||
val apiClient = FakeCardDetailApiClient().apply {
|
||||
updateCardResult = BoardsApiResult.Failure("Card is archived")
|
||||
}
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val result = repository.updateDescription(cardId = "card-1", description = " ")
|
||||
|
||||
assertEquals(null, apiClient.lastUpdatedDescriptionNormalized)
|
||||
assertTrue(result is CardDetailRepository.Result.Failure.Generic)
|
||||
assertEquals("Card is archived", (result as CardDetailRepository.Result.Failure.Generic).message)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateTitle_authFailureMapsToSessionExpired() = runTest {
|
||||
val apiClient = FakeCardDetailApiClient().apply {
|
||||
updateCardResult = BoardsApiResult.Failure("Server error: 401")
|
||||
}
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val result = repository.updateTitle(cardId = "card-1", title = " Updated title ")
|
||||
|
||||
assertTrue(result is CardDetailRepository.Result.Failure.SessionExpired)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listActivities_returnsNewestFirstTopTen() = runTest {
|
||||
val apiClient = FakeCardDetailApiClient().apply {
|
||||
listActivitiesResult = BoardsApiResult.Success((1..12).map { index ->
|
||||
CardActivity(
|
||||
id = "a-$index",
|
||||
type = "comment",
|
||||
text = "Activity $index",
|
||||
createdAtEpochMillis = index.toLong(),
|
||||
)
|
||||
})
|
||||
}
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val result = repository.listActivities("card-1")
|
||||
|
||||
assertTrue(result is CardDetailRepository.Result.Success)
|
||||
val activities = (result as CardDetailRepository.Result.Success<List<CardActivity>>).value
|
||||
assertEquals(10, activities.size)
|
||||
assertEquals("a-12", activities[0].id)
|
||||
assertEquals("a-3", activities[9].id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listActivities_authFailureMapsToSessionExpired() = runTest {
|
||||
val apiClient = FakeCardDetailApiClient().apply {
|
||||
listActivitiesResult = BoardsApiResult.Failure("Server error: 403")
|
||||
}
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val result = repository.listActivities("card-1")
|
||||
|
||||
assertTrue(result is CardDetailRepository.Result.Failure.SessionExpired)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addComment_success_refreshesActivities() = runTest {
|
||||
val apiClient = FakeCardDetailApiClient().apply {
|
||||
addCommentResult = BoardsApiResult.Success(Unit)
|
||||
listActivitiesResult = BoardsApiResult.Success(
|
||||
listOf(
|
||||
CardActivity(
|
||||
id = "a-1",
|
||||
type = "comment",
|
||||
text = "hello",
|
||||
createdAtEpochMillis = 1L,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val result = repository.addComment(cardId = "card-1", comment = " hello ")
|
||||
|
||||
assertTrue(result is CardDetailRepository.Result.Success)
|
||||
assertEquals(1, apiClient.addCommentCalls)
|
||||
assertEquals(1, apiClient.listActivitiesCalls)
|
||||
assertEquals("hello", apiClient.lastComment)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addComment_authFailureMapsToSessionExpired() = runTest {
|
||||
val apiClient = FakeCardDetailApiClient().apply {
|
||||
addCommentResult = BoardsApiResult.Failure("Authentication failed. Check your API key.")
|
||||
}
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val result = repository.addComment(cardId = "card-1", comment = "A")
|
||||
|
||||
assertTrue(result is CardDetailRepository.Result.Failure.SessionExpired)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateDueDate_dateOnlyInputNormalizesToUtcMidnightPayloadContract() = runTest {
|
||||
val apiClient = FakeCardDetailApiClient()
|
||||
val repository = createRepository(apiClient = apiClient)
|
||||
|
||||
val result = repository.updateDueDate(cardId = "card-1", dueDate = LocalDate.parse("2026-03-16"))
|
||||
|
||||
assertTrue(result is CardDetailRepository.Result.Success)
|
||||
assertEquals(LocalDate.of(2026, 3, 16), apiClient.lastUpdatedDueDate)
|
||||
}
|
||||
|
||||
private fun createRepository(
|
||||
sessionStore: InMemorySessionStore = InMemorySessionStore(baseUrl = "https://kan.bn/"),
|
||||
apiKeyStore: InMemoryApiKeyStore = InMemoryApiKeyStore("api-key"),
|
||||
apiClient: FakeCardDetailApiClient = FakeCardDetailApiClient(),
|
||||
): CardDetailRepository {
|
||||
return CardDetailRepository(
|
||||
sessionStore = sessionStore,
|
||||
apiKeyStore = apiKeyStore,
|
||||
apiClient = apiClient,
|
||||
)
|
||||
}
|
||||
|
||||
private class InMemorySessionStore(
|
||||
private var baseUrl: String?,
|
||||
private var workspaceId: String? = null,
|
||||
) : SessionStore {
|
||||
override fun getBaseUrl(): String? = baseUrl
|
||||
|
||||
override fun saveBaseUrl(url: String) {
|
||||
baseUrl = url
|
||||
}
|
||||
|
||||
override fun getWorkspaceId(): String? = workspaceId
|
||||
|
||||
override fun saveWorkspaceId(workspaceId: String) {
|
||||
this.workspaceId = workspaceId
|
||||
}
|
||||
|
||||
override fun clearBaseUrl() {
|
||||
baseUrl = null
|
||||
}
|
||||
|
||||
override fun clearWorkspaceId() {
|
||||
workspaceId = null
|
||||
}
|
||||
}
|
||||
|
||||
private class InMemoryApiKeyStore(private var apiKey: String?) : ApiKeyStore {
|
||||
override suspend fun saveApiKey(baseUrl: String, apiKey: String): Result<Unit> {
|
||||
this.apiKey = apiKey
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun getApiKey(baseUrl: String): Result<String?> = Result.success(apiKey)
|
||||
|
||||
override suspend fun invalidateApiKey(baseUrl: String): Result<Unit> {
|
||||
apiKey = null
|
||||
return Result.success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeCardDetailApiClient : KanbnApiClient {
|
||||
var cardDetailResult: BoardsApiResult<CardDetail> = BoardsApiResult.Success(
|
||||
CardDetail(
|
||||
id = "card-1",
|
||||
title = "Current title",
|
||||
description = "Current description",
|
||||
dueDate = LocalDate.of(2026, 3, 1),
|
||||
listPublicId = "list-1",
|
||||
index = 0,
|
||||
tags = emptyList(),
|
||||
),
|
||||
)
|
||||
var updateCardResult: BoardsApiResult<Unit> = BoardsApiResult.Success(Unit)
|
||||
var listActivitiesResult: BoardsApiResult<List<CardActivity>> = BoardsApiResult.Success(emptyList())
|
||||
var addCommentResult: BoardsApiResult<Unit> = BoardsApiResult.Success(Unit)
|
||||
|
||||
var lastUpdatedTitle: String? = null
|
||||
var lastUpdatedDescription: String? = null
|
||||
var lastUpdatedDescriptionNormalized: String? = null
|
||||
var lastUpdatedDueDate: LocalDate? = null
|
||||
var addCommentCalls: Int = 0
|
||||
var listActivitiesCalls: Int = 0
|
||||
var lastComment: String? = null
|
||||
|
||||
override suspend fun healthCheck(baseUrl: String, apiKey: String): AuthResult = AuthResult.Success
|
||||
|
||||
override suspend fun getCardDetail(baseUrl: String, apiKey: String, cardId: String): BoardsApiResult<CardDetail> {
|
||||
return cardDetailResult
|
||||
}
|
||||
|
||||
override suspend fun updateCard(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
cardId: String,
|
||||
title: String,
|
||||
description: String,
|
||||
dueDate: LocalDate?,
|
||||
): BoardsApiResult<Unit> {
|
||||
lastUpdatedTitle = title
|
||||
lastUpdatedDescription = description
|
||||
lastUpdatedDescriptionNormalized = description.takeIf { it.isNotBlank() }
|
||||
lastUpdatedDueDate = dueDate
|
||||
return updateCardResult
|
||||
}
|
||||
|
||||
override suspend fun listCardActivities(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
cardId: String,
|
||||
): BoardsApiResult<List<CardActivity>> {
|
||||
listActivitiesCalls += 1
|
||||
return listActivitiesResult
|
||||
}
|
||||
|
||||
override suspend fun addCardComment(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
cardId: String,
|
||||
comment: String,
|
||||
): BoardsApiResult<Unit> {
|
||||
addCommentCalls += 1
|
||||
lastComment = comment
|
||||
return addCommentResult
|
||||
}
|
||||
|
||||
override suspend fun listWorkspaces(baseUrl: String, apiKey: String): BoardsApiResult<List<WorkspaceSummary>> {
|
||||
return BoardsApiResult.Success(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun listBoards(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
workspaceId: String,
|
||||
): BoardsApiResult<List<BoardSummary>> {
|
||||
return BoardsApiResult.Success(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun listBoardTemplates(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
workspaceId: String,
|
||||
): BoardsApiResult<List<BoardTemplate>> {
|
||||
return BoardsApiResult.Success(emptyList())
|
||||
}
|
||||
|
||||
override suspend fun createBoard(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
workspaceId: String,
|
||||
name: String,
|
||||
templateId: String?,
|
||||
): BoardsApiResult<BoardSummary> {
|
||||
return BoardsApiResult.Success(BoardSummary("board-1", name))
|
||||
}
|
||||
|
||||
override suspend fun deleteBoard(baseUrl: String, apiKey: String, boardId: String): BoardsApiResult<Unit> {
|
||||
return BoardsApiResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun getBoardDetail(baseUrl: String, apiKey: String, boardId: String): BoardsApiResult<BoardDetail> {
|
||||
return BoardsApiResult.Success(BoardDetail(id = boardId, title = "Board", lists = emptyList()))
|
||||
}
|
||||
|
||||
override suspend fun renameList(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
listId: String,
|
||||
newTitle: String,
|
||||
): BoardsApiResult<Unit> {
|
||||
return BoardsApiResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun createList(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
boardPublicId: String,
|
||||
title: String,
|
||||
appendIndex: Int,
|
||||
): BoardsApiResult<CreatedEntityRef> {
|
||||
return BoardsApiResult.Success(CreatedEntityRef("list-1"))
|
||||
}
|
||||
|
||||
override suspend fun createCard(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
listPublicId: String,
|
||||
title: String,
|
||||
description: String?,
|
||||
dueDate: LocalDate?,
|
||||
tagPublicIds: List<String>,
|
||||
): BoardsApiResult<CreatedEntityRef> {
|
||||
return BoardsApiResult.Success(CreatedEntityRef("card-1"))
|
||||
}
|
||||
|
||||
override suspend fun moveCard(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
cardId: String,
|
||||
targetListId: String,
|
||||
): BoardsApiResult<Unit> {
|
||||
return BoardsApiResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun deleteCard(baseUrl: String, apiKey: String, cardId: String): BoardsApiResult<Unit> {
|
||||
return BoardsApiResult.Success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun getLabelByPublicId(
|
||||
baseUrl: String,
|
||||
apiKey: String,
|
||||
labelId: String,
|
||||
): BoardsApiResult<LabelDetail> {
|
||||
return BoardsApiResult.Success(LabelDetail(labelId, "#000000"))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user