PM-19241 folder result errors propagated to UI (#4870)

This commit is contained in:
Dave Severns
2025-03-17 08:20:44 -04:00
committed by GitHub
parent db287ddce5
commit dfcdc72499
8 changed files with 65 additions and 34 deletions

View File

@@ -788,7 +788,8 @@ class VaultRepositoryImpl(
}
override suspend fun createFolder(folderView: FolderView): CreateFolderResult {
val userId = activeUserId ?: return CreateFolderResult.Error
val userId = activeUserId
?: return CreateFolderResult.Error(error = NoActiveUserException())
return vaultSdkSource
.encryptFolder(
userId = userId,
@@ -804,7 +805,7 @@ class VaultRepositoryImpl(
.flatMap { vaultSdkSource.decryptFolder(userId, it.toEncryptedSdkFolder()) }
.fold(
onSuccess = { CreateFolderResult.Success(folderView = it) },
onFailure = { CreateFolderResult.Error },
onFailure = { CreateFolderResult.Error(error = it) },
)
}
@@ -812,7 +813,10 @@ class VaultRepositoryImpl(
folderId: String,
folderView: FolderView,
): UpdateFolderResult {
val userId = activeUserId ?: return UpdateFolderResult.Error(null)
val userId = activeUserId ?: return UpdateFolderResult.Error(
errorMessage = null,
error = NoActiveUserException(),
)
return vaultSdkSource
.encryptFolder(
userId = userId,
@@ -837,21 +841,30 @@ class VaultRepositoryImpl(
)
.fold(
onSuccess = { UpdateFolderResult.Success(it) },
onFailure = { UpdateFolderResult.Error(errorMessage = null) },
onFailure = {
UpdateFolderResult.Error(
errorMessage = null,
error = it,
)
},
)
}
is UpdateFolderResponseJson.Invalid -> {
UpdateFolderResult.Error(response.message)
UpdateFolderResult.Error(
errorMessage = response.message,
error = null,
)
}
}
},
onFailure = { UpdateFolderResult.Error(it.message) },
onFailure = { UpdateFolderResult.Error(it.message, error = it) },
)
}
override suspend fun deleteFolder(folderId: String): DeleteFolderResult {
val userId = activeUserId ?: return DeleteFolderResult.Error
val userId = activeUserId
?: return DeleteFolderResult.Error(error = NoActiveUserException())
return folderService
.deleteFolder(
folderId = folderId,
@@ -862,7 +875,7 @@ class VaultRepositoryImpl(
}
.fold(
onSuccess = { DeleteFolderResult.Success },
onFailure = { DeleteFolderResult.Error },
onFailure = { DeleteFolderResult.Error(error = it) },
)
}

View File

@@ -15,5 +15,5 @@ sealed class CreateFolderResult {
/**
* Generic error while creating a folder.
*/
data object Error : CreateFolderResult()
data class Error(val error: Throwable) : CreateFolderResult()
}

View File

@@ -13,5 +13,5 @@ sealed class DeleteFolderResult {
/**
* Generic error while deleting a folder.
*/
data object Error : DeleteFolderResult()
data class Error(val error: Throwable) : DeleteFolderResult()
}

View File

@@ -16,5 +16,5 @@ sealed class UpdateFolderResult {
* Generic error while updating a folder. The optional [errorMessage]
* may be displayed directly in the UI when present.
*/
data class Error(val errorMessage: String?) : UpdateFolderResult()
data class Error(val errorMessage: String?, val error: Throwable?) : UpdateFolderResult()
}

View File

@@ -178,6 +178,7 @@ private fun FolderAddEditItemDialogs(
title = stringResource(id = R.string.an_error_has_occurred),
message = dialogState.message(),
onDismissRequest = onDismissRequest,
throwable = dialogState.throwable,
)
null -> Unit

View File

@@ -250,12 +250,13 @@ class FolderAddEditViewModel @Inject constructor(
it.copy(dialog = null)
}
when (action.result) {
when (val result = action.result) {
is UpdateFolderResult.Error -> {
mutableStateFlow.update {
it.copy(
dialog = FolderAddEditState.DialogState.Error(
message = R.string.generic_error_message.asText(),
throwable = result.error,
),
)
}
@@ -275,12 +276,13 @@ class FolderAddEditViewModel @Inject constructor(
it.copy(dialog = null)
}
when (action.result) {
when (val result = action.result) {
is CreateFolderResult.Error -> {
mutableStateFlow.update {
it.copy(
dialog = FolderAddEditState.DialogState.Error(
message = R.string.generic_error_message.asText(),
throwable = result.error,
),
)
}
@@ -296,12 +298,13 @@ class FolderAddEditViewModel @Inject constructor(
private fun handleDeleteResultReceive(
action: FolderAddEditAction.Internal.DeleteFolderResultReceive,
) {
when (action.result) {
DeleteFolderResult.Error -> {
when (val result = action.result) {
is DeleteFolderResult.Error -> {
mutableStateFlow.update {
it.copy(
dialog = FolderAddEditState.DialogState.Error(
message = R.string.generic_error_message.asText(),
throwable = result.error,
),
)
}
@@ -398,6 +401,7 @@ data class FolderAddEditState(
@Parcelize
data class Error(
val message: Text,
val throwable: Throwable? = null,
) : DialogState()
}
}

View File

@@ -2724,7 +2724,7 @@ class VaultRepositoryTest {
val result = vaultRepository.deleteFolder("Test")
assertEquals(
DeleteFolderResult.Error,
DeleteFolderResult.Error(error = NoActiveUserException()),
result,
)
}
@@ -2734,11 +2734,12 @@ class VaultRepositoryTest {
fun `DeleteFolder with folderService Delete failure should return DeleteFolderResult Failure`() =
runTest {
fakeAuthDiskSource.userState = MOCK_USER_STATE
val error = Throwable("fail")
val folderId = "mockId-1"
coEvery { folderService.deleteFolder(folderId) } returns Throwable("fail").asFailure()
coEvery { folderService.deleteFolder(folderId) } returns error.asFailure()
val result = vaultRepository.deleteFolder(folderId)
assertEquals(DeleteFolderResult.Error, result)
assertEquals(DeleteFolderResult.Error(error = error), result)
}
@Suppress("MaxLineLength")
@@ -2800,7 +2801,7 @@ class VaultRepositoryTest {
val result = vaultRepository.createFolder(mockk())
assertEquals(
CreateFolderResult.Error,
CreateFolderResult.Error(NoActiveUserException()),
result,
)
}
@@ -2811,10 +2812,11 @@ class VaultRepositoryTest {
runTest {
fakeAuthDiskSource.userState = MOCK_USER_STATE
val folderId = "mockId-1"
coEvery { folderService.deleteFolder(folderId) } returns Throwable("fail").asFailure()
val error = Throwable("fail")
coEvery { folderService.deleteFolder(folderId) } returns error.asFailure()
val result = vaultRepository.deleteFolder(folderId)
assertEquals(DeleteFolderResult.Error, result)
assertEquals(DeleteFolderResult.Error(error = error), result)
}
@Test
@@ -2826,16 +2828,17 @@ class VaultRepositoryTest {
name = "TestName",
revisionDate = DateTime.now(),
)
val error = IllegalStateException()
coEvery {
vaultSdkSource.encryptFolder(
userId = MOCK_USER_STATE.activeUserId,
folder = folderView,
)
} returns IllegalStateException().asFailure()
} returns error.asFailure()
val result = vaultRepository.createFolder(folderView)
assertEquals(CreateFolderResult.Error, result)
assertEquals(CreateFolderResult.Error(error = error), result)
}
@Test
@@ -2850,6 +2853,7 @@ class VaultRepositoryTest {
name = testFolderName,
revisionDate = date,
)
val error = IllegalStateException()
coEvery {
vaultSdkSource.encryptFolder(
@@ -2862,10 +2866,10 @@ class VaultRepositoryTest {
folderService.createFolder(
body = FolderJsonRequest(testFolderName),
)
} returns IllegalStateException().asFailure()
} returns error.asFailure()
val result = vaultRepository.createFolder(folderView)
assertEquals(CreateFolderResult.Error, result)
assertEquals(CreateFolderResult.Error(error = error), result)
}
@Test
@@ -2926,7 +2930,7 @@ class VaultRepositoryTest {
val result = vaultRepository.updateFolder("Test", mockk())
assertEquals(
UpdateFolderResult.Error(null),
UpdateFolderResult.Error(errorMessage = null, error = NoActiveUserException()),
result,
)
}
@@ -2941,17 +2945,18 @@ class VaultRepositoryTest {
name = "TestName",
revisionDate = DateTime.now(),
)
val error = IllegalStateException()
coEvery {
vaultSdkSource.encryptFolder(
userId = MOCK_USER_STATE.activeUserId,
folder = folderView,
)
} returns IllegalStateException().asFailure()
} returns error.asFailure()
val result = vaultRepository.updateFolder(folderId, folderView)
assertEquals(UpdateFolderResult.Error(errorMessage = null), result)
assertEquals(UpdateFolderResult.Error(errorMessage = null, error = error), result)
}
@Test
@@ -2967,6 +2972,7 @@ class VaultRepositoryTest {
name = testFolderName,
revisionDate = date,
)
val error = IllegalStateException()
coEvery {
vaultSdkSource.encryptFolder(
@@ -2980,10 +2986,10 @@ class VaultRepositoryTest {
folderId = folderId,
body = FolderJsonRequest(testFolderName),
)
} returns IllegalStateException().asFailure()
} returns error.asFailure()
val result = vaultRepository.updateFolder(folderId, folderView)
assertEquals(UpdateFolderResult.Error(errorMessage = null), result)
assertEquals(UpdateFolderResult.Error(errorMessage = null, error = error), result)
}
@Suppress("MaxLineLength")
@@ -3024,6 +3030,7 @@ class VaultRepositoryTest {
assertEquals(
UpdateFolderResult.Error(
errorMessage = "You do not have permission to edit this.",
error = null,
),
result,
)

View File

@@ -199,10 +199,12 @@ class FolderAddEditViewModelTest : BaseViewModelTest() {
@Test
fun `DeleteClick with DeleteFolderResult Failure should show an error dialog`() =
runTest {
val error = Throwable("Oops")
val stateWithDialog = FolderAddEditState(
folderAddEditType = FolderAddEditType.EditItem((DEFAULT_EDIT_ITEM_ID)),
dialog = FolderAddEditState.DialogState.Error(
R.string.generic_error_message.asText(),
throwable = error,
),
viewState = FolderAddEditState.ViewState.Content(
folderName = DEFAULT_FOLDER_NAME,
@@ -231,7 +233,7 @@ class FolderAddEditViewModelTest : BaseViewModelTest() {
coEvery {
vaultRepository.deleteFolder(folderId = DEFAULT_EDIT_ITEM_ID)
} returns DeleteFolderResult.Error
} returns DeleteFolderResult.Error(error = error)
viewModel.trySendAction(FolderAddEditAction.DeleteClick)
@@ -404,9 +406,10 @@ class FolderAddEditViewModelTest : BaseViewModelTest() {
),
)
val error = Throwable("Oops")
coEvery {
vaultRepository.createFolder(any())
} returns CreateFolderResult.Error
} returns CreateFolderResult.Error(error = error)
viewModel.trySendAction(FolderAddEditAction.SaveClick)
@@ -414,6 +417,7 @@ class FolderAddEditViewModelTest : BaseViewModelTest() {
state.copy(
dialog = FolderAddEditState.DialogState.Error(
R.string.generic_error_message.asText(),
throwable = error,
),
),
viewModel.stateFlow.value,
@@ -485,6 +489,7 @@ class FolderAddEditViewModelTest : BaseViewModelTest() {
state = state,
),
)
val error = Throwable("Oops")
mutableFoldersStateFlow.value =
DataState.Loaded(
@@ -497,14 +502,15 @@ class FolderAddEditViewModelTest : BaseViewModelTest() {
coEvery {
vaultRepository.updateFolder(any(), any())
} returns UpdateFolderResult.Error(errorMessage = null)
} returns UpdateFolderResult.Error(errorMessage = null, error = error)
viewModel.trySendAction(FolderAddEditAction.SaveClick)
assertEquals(
state.copy(
dialog = FolderAddEditState.DialogState.Error(
R.string.generic_error_message.asText(),
message = R.string.generic_error_message.asText(),
throwable = error,
),
),
viewModel.stateFlow.value,