BIT-1246, BIT-1250: Show correct permission-related errors when editing (#482)

This commit is contained in:
Brian Yencho
2024-01-03 10:15:21 -06:00
committed by Álison Fernandes
parent 8476e55b5a
commit 1e996fcbbe
11 changed files with 244 additions and 58 deletions

View File

@@ -9,6 +9,7 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import retrofit2.create
import javax.inject.Singleton
@@ -23,8 +24,10 @@ object VaultNetworkModule {
@Singleton
fun provideCiphersService(
retrofits: Retrofits,
json: Json,
): CiphersService = CiphersServiceImpl(
ciphersApi = retrofits.authenticatedApiRetrofit.create(),
json = json,
)
@Provides

View File

@@ -0,0 +1,33 @@
package com.x8bit.bitwarden.data.vault.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
/**
* Models the response from the update cipher request.
*/
sealed class UpdateCipherResponseJson {
/**
* The request completed successfully and returned the updated [cipher].
*/
data class Success(
val cipher: SyncResponseJson.Cipher,
) : UpdateCipherResponseJson()
/**
* Represents the json body of an invalid update request.
*
* @param message A general, user-displayable error message.
* @param validationErrors a map where each value is a list of error messages for each key.
* The values in the array should be used for display to the user, since the keys tend to come
* back as nonsense. (eg: empty string key)
*/
@Serializable
data class Invalid(
@SerialName("message")
val message: String?,
@SerialName("validationErrors")
val validationErrors: Map<String, List<String>>?,
) : UpdateCipherResponseJson()
}

View File

@@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.vault.datasource.network.service
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateCipherResponseJson
/**
* Provides an API for querying ciphers endpoints.
@@ -18,5 +19,5 @@ interface CiphersService {
suspend fun updateCipher(
cipherId: String,
body: CipherJsonRequest,
): Result<SyncResponseJson.Cipher>
): Result<UpdateCipherResponseJson>
}

View File

@@ -1,11 +1,16 @@
package com.x8bit.bitwarden.data.vault.datasource.network.service
import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenError
import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull
import com.x8bit.bitwarden.data.vault.datasource.network.api.CiphersApi
import com.x8bit.bitwarden.data.vault.datasource.network.model.CipherJsonRequest
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateCipherResponseJson
import kotlinx.serialization.json.Json
class CiphersServiceImpl constructor(
private val ciphersApi: CiphersApi,
private val json: Json,
) : CiphersService {
override suspend fun createCipher(body: CipherJsonRequest): Result<SyncResponseJson.Cipher> =
ciphersApi.createCipher(body = body)
@@ -13,9 +18,20 @@ class CiphersServiceImpl constructor(
override suspend fun updateCipher(
cipherId: String,
body: CipherJsonRequest,
): Result<SyncResponseJson.Cipher> =
ciphersApi.updateCipher(
cipherId = cipherId,
body = body,
)
): Result<UpdateCipherResponseJson> =
ciphersApi
.updateCipher(
cipherId = cipherId,
body = body,
)
.map { UpdateCipherResponseJson.Success(cipher = it) }
.recoverCatching { throwable ->
throwable
.toBitwardenError()
.parseErrorBodyOrNull<UpdateCipherResponseJson.Invalid>(
code = 400,
json = json,
)
?: throw throwable
}
}

View File

@@ -22,6 +22,7 @@ import com.x8bit.bitwarden.data.platform.util.asSuccess
import com.x8bit.bitwarden.data.platform.util.flatMap
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateCipherResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.service.CiphersService
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
@@ -387,10 +388,18 @@ class VaultRepositoryImpl(
)
}
.fold(
onFailure = { UpdateCipherResult.Error },
onSuccess = {
sync()
UpdateCipherResult.Success
onFailure = { UpdateCipherResult.Error(errorMessage = null) },
onSuccess = { response ->
when (response) {
is UpdateCipherResponseJson.Invalid -> {
UpdateCipherResult.Error(errorMessage = response.message)
}
is UpdateCipherResponseJson.Success -> {
sync()
UpdateCipherResult.Success
}
}
},
)

View File

@@ -11,7 +11,8 @@ sealed class UpdateCipherResult {
data object Success : UpdateCipherResult()
/**
* Generic error while updating cipher.
* Generic error while updating cipher. The optional [errorMessage] may be displayed directly in
* the UI when present.
*/
data object Error : UpdateCipherResult()
data class Error(val errorMessage: String?) : UpdateCipherResult()
}

View File

@@ -690,10 +690,18 @@ class VaultAddEditViewModel @Inject constructor(
action: VaultAddEditAction.Internal.UpdateCipherResultReceive,
) {
mutableStateFlow.update { it.copy(dialog = null) }
when (action.updateCipherResult) {
when (val result = action.updateCipherResult) {
is UpdateCipherResult.Error -> {
// TODO Display error dialog BIT-501
sendEvent(VaultAddEditEvent.ShowToast(message = "Save Item Failure".asText()))
mutableStateFlow.update {
it.copy(
dialog = VaultAddEditState.DialogState.Error(
message = result
.errorMessage
?.asText()
?: R.string.generic_error_message.asText(),
),
)
}
}
is UpdateCipherResult.Success -> {