mirror of
https://github.com/bitwarden/android.git
synced 2026-03-11 20:54:58 -05:00
[PM-21782] Improve create cipher error handling (#5362)
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
package com.bitwarden.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Models the response from the create cipher request.
|
||||
*/
|
||||
sealed class CreateCipherResponseJson {
|
||||
|
||||
/**
|
||||
* The request completed successfully and returned the created [cipher].
|
||||
*/
|
||||
data class Success(val cipher: SyncResponseJson.Cipher) : CreateCipherResponseJson()
|
||||
|
||||
/**
|
||||
* Represents the json body of an invalid create 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>>?,
|
||||
) : CreateCipherResponseJson()
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import com.bitwarden.network.model.AttachmentJsonRequest
|
||||
import com.bitwarden.network.model.AttachmentJsonResponse
|
||||
import com.bitwarden.network.model.CipherJsonRequest
|
||||
import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest
|
||||
import com.bitwarden.network.model.CreateCipherResponseJson
|
||||
import com.bitwarden.network.model.ImportCiphersJsonRequest
|
||||
import com.bitwarden.network.model.ImportCiphersResponseJson
|
||||
import com.bitwarden.network.model.ShareCipherJsonRequest
|
||||
@@ -21,14 +22,14 @@ interface CiphersService {
|
||||
/**
|
||||
* Attempt to create a cipher.
|
||||
*/
|
||||
suspend fun createCipher(body: CipherJsonRequest): Result<SyncResponseJson.Cipher>
|
||||
suspend fun createCipher(body: CipherJsonRequest): Result<CreateCipherResponseJson>
|
||||
|
||||
/**
|
||||
* Attempt to create a cipher that belongs to an organization.
|
||||
*/
|
||||
suspend fun createCipherInOrganization(
|
||||
body: CreateCipherInOrganizationJsonRequest,
|
||||
): Result<SyncResponseJson.Cipher>
|
||||
): Result<CreateCipherResponseJson>
|
||||
|
||||
/**
|
||||
* Attempt to upload an attachment file.
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.bitwarden.network.model.AttachmentJsonRequest
|
||||
import com.bitwarden.network.model.AttachmentJsonResponse
|
||||
import com.bitwarden.network.model.CipherJsonRequest
|
||||
import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest
|
||||
import com.bitwarden.network.model.CreateCipherResponseJson
|
||||
import com.bitwarden.network.model.FileUploadType
|
||||
import com.bitwarden.network.model.ImportCiphersJsonRequest
|
||||
import com.bitwarden.network.model.ImportCiphersResponseJson
|
||||
@@ -36,16 +37,38 @@ internal class CiphersServiceImpl(
|
||||
private val json: Json,
|
||||
private val clock: Clock,
|
||||
) : CiphersService {
|
||||
override suspend fun createCipher(body: CipherJsonRequest): Result<SyncResponseJson.Cipher> =
|
||||
override suspend fun createCipher(
|
||||
body: CipherJsonRequest,
|
||||
): Result<CreateCipherResponseJson> =
|
||||
ciphersApi
|
||||
.createCipher(body = body)
|
||||
.toResult()
|
||||
.map { CreateCipherResponseJson.Success(it) }
|
||||
.recoverCatching { throwable ->
|
||||
throwable
|
||||
.toBitwardenError()
|
||||
.parseErrorBodyOrNull<CreateCipherResponseJson.Invalid>(
|
||||
code = NetworkErrorCode.BAD_REQUEST,
|
||||
json = json,
|
||||
)
|
||||
?: throw throwable
|
||||
}
|
||||
|
||||
override suspend fun createCipherInOrganization(
|
||||
body: CreateCipherInOrganizationJsonRequest,
|
||||
): Result<SyncResponseJson.Cipher> = ciphersApi
|
||||
): Result<CreateCipherResponseJson> = ciphersApi
|
||||
.createCipherInOrganization(body = body)
|
||||
.toResult()
|
||||
.map { CreateCipherResponseJson.Success(it) }
|
||||
.recoverCatching { throwable ->
|
||||
throwable
|
||||
.toBitwardenError()
|
||||
.parseErrorBodyOrNull<CreateCipherResponseJson.Invalid>(
|
||||
code = NetworkErrorCode.BAD_REQUEST,
|
||||
json = json,
|
||||
)
|
||||
?: throw throwable
|
||||
}
|
||||
|
||||
override suspend fun createAttachment(
|
||||
cipherId: String,
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.bitwarden.network.api.CiphersApi
|
||||
import com.bitwarden.network.base.BaseServiceTest
|
||||
import com.bitwarden.network.model.AttachmentJsonResponse
|
||||
import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest
|
||||
import com.bitwarden.network.model.CreateCipherResponseJson
|
||||
import com.bitwarden.network.model.FileUploadType
|
||||
import com.bitwarden.network.model.ImportCiphersJsonRequest
|
||||
import com.bitwarden.network.model.ImportCiphersResponseJson
|
||||
@@ -67,7 +68,22 @@ class CiphersServiceTest : BaseServiceTest() {
|
||||
body = createMockCipherJsonRequest(number = 1),
|
||||
)
|
||||
assertEquals(
|
||||
createMockCipher(number = 1),
|
||||
CreateCipherResponseJson.Success(createMockCipher(number = 1)),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createCipher should return Invalid with correct data`() = runTest {
|
||||
server.enqueue(MockResponse().setResponseCode(400).setBody(CREATE_CIPHER_INVALID_JSON))
|
||||
val result = ciphersService.createCipher(
|
||||
body = createMockCipherJsonRequest(number = 1),
|
||||
)
|
||||
assertEquals(
|
||||
CreateCipherResponseJson.Invalid(
|
||||
message = "Cipher was not encrypted for the current user. Please try again.",
|
||||
validationErrors = null,
|
||||
),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
@@ -82,11 +98,30 @@ class CiphersServiceTest : BaseServiceTest() {
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
createMockCipher(number = 1),
|
||||
CreateCipherResponseJson.Success(createMockCipher(number = 1)),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createCipherInOrganization should return Invalid with correct data`() =
|
||||
runTest {
|
||||
server.enqueue(MockResponse().setResponseCode(400).setBody(CREATE_CIPHER_INVALID_JSON))
|
||||
val result = ciphersService.createCipherInOrganization(
|
||||
body = CreateCipherInOrganizationJsonRequest(
|
||||
cipher = createMockCipherJsonRequest(number = 1),
|
||||
collectionIds = listOf("12345"),
|
||||
),
|
||||
)
|
||||
assertEquals(
|
||||
CreateCipherResponseJson.Invalid(
|
||||
message = "Cipher was not encrypted for the current user. Please try again.",
|
||||
validationErrors = null,
|
||||
),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createAttachment should return the correct response`() = runTest {
|
||||
server.enqueue(MockResponse().setBody(CREATE_ATTACHMENT_SUCCESS_JSON))
|
||||
@@ -606,6 +641,12 @@ private const val UPDATE_CIPHER_INVALID_JSON = """
|
||||
"validationErrors": null
|
||||
}
|
||||
"""
|
||||
private const val CREATE_CIPHER_INVALID_JSON = """
|
||||
{
|
||||
"message": "Cipher was not encrypted for the current user. Please try again.",
|
||||
"validationErrors": null
|
||||
}
|
||||
"""
|
||||
|
||||
private const val GET_CIPHER_ATTACHMENT_SUCCESS_JSON = """
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user