mirror of
https://github.com/bitwarden/android.git
synced 2026-03-21 22:00:42 -05:00
PM-19241 folder result errors propagated to UI (#4870)
This commit is contained in:
@@ -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) },
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,5 +15,5 @@ sealed class CreateFolderResult {
|
||||
/**
|
||||
* Generic error while creating a folder.
|
||||
*/
|
||||
data object Error : CreateFolderResult()
|
||||
data class Error(val error: Throwable) : CreateFolderResult()
|
||||
}
|
||||
|
||||
@@ -13,5 +13,5 @@ sealed class DeleteFolderResult {
|
||||
/**
|
||||
* Generic error while deleting a folder.
|
||||
*/
|
||||
data object Error : DeleteFolderResult()
|
||||
data class Error(val error: Throwable) : DeleteFolderResult()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user