mirror of
https://github.com/bitwarden/android.git
synced 2026-03-11 20:54:58 -05:00
[PM-32802] fix: 400 error when archiving/unarchiving org-owned ciphers (#6592)
This commit is contained in:
@@ -32,7 +32,7 @@ internal interface CiphersApi {
|
||||
@PUT("ciphers/{cipherId}/archive")
|
||||
suspend fun archiveCipher(
|
||||
@Path("cipherId") cipherId: String,
|
||||
): NetworkResult<Unit>
|
||||
): NetworkResult<SyncResponseJson.Cipher>
|
||||
|
||||
/**
|
||||
* Unarchive a cipher.
|
||||
@@ -40,7 +40,7 @@ internal interface CiphersApi {
|
||||
@PUT("ciphers/{cipherId}/unarchive")
|
||||
suspend fun unarchiveCipher(
|
||||
@Path("cipherId") cipherId: String,
|
||||
): NetworkResult<Unit>
|
||||
): NetworkResult<SyncResponseJson.Cipher>
|
||||
|
||||
/**
|
||||
* Create a cipher.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.bitwarden.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Models the response from the archive cipher request.
|
||||
*/
|
||||
sealed class ArchiveCipherResponseJson {
|
||||
/**
|
||||
* The request completed successfully and returned the archived [cipher].
|
||||
*/
|
||||
data class Success(
|
||||
val cipher: SyncResponseJson.Cipher,
|
||||
) : ArchiveCipherResponseJson()
|
||||
|
||||
/**
|
||||
* Represents the json body of an invalid archive 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")
|
||||
override val message: String,
|
||||
|
||||
@SerialName("validationErrors")
|
||||
override val validationErrors: Map<String, List<String>>?,
|
||||
) : ArchiveCipherResponseJson(), InvalidJsonResponse
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.bitwarden.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Models the response from the unarchive cipher request.
|
||||
*/
|
||||
sealed class UnarchiveCipherResponseJson {
|
||||
/**
|
||||
* The request completed successfully and returned the unarchived [cipher].
|
||||
*/
|
||||
data class Success(
|
||||
val cipher: SyncResponseJson.Cipher,
|
||||
) : UnarchiveCipherResponseJson()
|
||||
|
||||
/**
|
||||
* Represents the json body of an invalid unarchive 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")
|
||||
override val message: String,
|
||||
|
||||
@SerialName("validationErrors")
|
||||
override val validationErrors: Map<String, List<String>>?,
|
||||
) : UnarchiveCipherResponseJson(), InvalidJsonResponse
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.bitwarden.network.service
|
||||
|
||||
import com.bitwarden.network.model.ArchiveCipherResponseJson
|
||||
import com.bitwarden.network.model.AttachmentInfo
|
||||
import com.bitwarden.network.model.AttachmentJsonRequest
|
||||
import com.bitwarden.network.model.AttachmentJsonResponse
|
||||
@@ -12,6 +13,7 @@ import com.bitwarden.network.model.ImportCiphersJsonRequest
|
||||
import com.bitwarden.network.model.ImportCiphersResponseJson
|
||||
import com.bitwarden.network.model.ShareCipherJsonRequest
|
||||
import com.bitwarden.network.model.SyncResponseJson
|
||||
import com.bitwarden.network.model.UnarchiveCipherResponseJson
|
||||
import com.bitwarden.network.model.UpdateCipherCollectionsJsonRequest
|
||||
import com.bitwarden.network.model.UpdateCipherResponseJson
|
||||
import java.io.File
|
||||
@@ -24,12 +26,12 @@ interface CiphersService {
|
||||
/**
|
||||
* Attempt to archive a cipher.
|
||||
*/
|
||||
suspend fun archiveCipher(cipherId: String): Result<Unit>
|
||||
suspend fun archiveCipher(cipherId: String): Result<ArchiveCipherResponseJson>
|
||||
|
||||
/**
|
||||
* Attempt to unarchive a cipher.
|
||||
*/
|
||||
suspend fun unarchiveCipher(cipherId: String): Result<Unit>
|
||||
suspend fun unarchiveCipher(cipherId: String): Result<UnarchiveCipherResponseJson>
|
||||
|
||||
/**
|
||||
* Attempt to create a cipher.
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.bitwarden.network.service
|
||||
import androidx.core.net.toUri
|
||||
import com.bitwarden.network.api.AzureApi
|
||||
import com.bitwarden.network.api.CiphersApi
|
||||
import com.bitwarden.network.model.ArchiveCipherResponseJson
|
||||
import com.bitwarden.network.model.AttachmentInfo
|
||||
import com.bitwarden.network.model.AttachmentJsonRequest
|
||||
import com.bitwarden.network.model.AttachmentJsonResponse
|
||||
@@ -16,6 +17,7 @@ import com.bitwarden.network.model.ImportCiphersJsonRequest
|
||||
import com.bitwarden.network.model.ImportCiphersResponseJson
|
||||
import com.bitwarden.network.model.ShareCipherJsonRequest
|
||||
import com.bitwarden.network.model.SyncResponseJson
|
||||
import com.bitwarden.network.model.UnarchiveCipherResponseJson
|
||||
import com.bitwarden.network.model.UpdateCipherCollectionsJsonRequest
|
||||
import com.bitwarden.network.model.UpdateCipherResponseJson
|
||||
import com.bitwarden.network.model.toBitwardenError
|
||||
@@ -40,15 +42,37 @@ internal class CiphersServiceImpl(
|
||||
) : CiphersService {
|
||||
override suspend fun archiveCipher(
|
||||
cipherId: String,
|
||||
): Result<Unit> = ciphersApi
|
||||
.archiveCipher(cipherId = cipherId)
|
||||
.toResult()
|
||||
): Result<ArchiveCipherResponseJson> =
|
||||
ciphersApi
|
||||
.archiveCipher(cipherId = cipherId)
|
||||
.toResult()
|
||||
.map { ArchiveCipherResponseJson.Success(cipher = it) }
|
||||
.recoverCatching { throwable ->
|
||||
throwable
|
||||
.toBitwardenError()
|
||||
.parseErrorBodyOrNull<ArchiveCipherResponseJson.Invalid>(
|
||||
code = NetworkErrorCode.BAD_REQUEST,
|
||||
json = json,
|
||||
)
|
||||
?: throw throwable
|
||||
}
|
||||
|
||||
override suspend fun unarchiveCipher(
|
||||
cipherId: String,
|
||||
): Result<Unit> = ciphersApi
|
||||
.unarchiveCipher(cipherId = cipherId)
|
||||
.toResult()
|
||||
): Result<UnarchiveCipherResponseJson> =
|
||||
ciphersApi
|
||||
.unarchiveCipher(cipherId = cipherId)
|
||||
.toResult()
|
||||
.map { UnarchiveCipherResponseJson.Success(cipher = it) }
|
||||
.recoverCatching { throwable ->
|
||||
throwable
|
||||
.toBitwardenError()
|
||||
.parseErrorBodyOrNull<UnarchiveCipherResponseJson.Invalid>(
|
||||
code = NetworkErrorCode.BAD_REQUEST,
|
||||
json = json,
|
||||
)
|
||||
?: throw throwable
|
||||
}
|
||||
|
||||
override suspend fun createCipher(
|
||||
body: CipherJsonRequest,
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.net.Uri
|
||||
import com.bitwarden.network.api.AzureApi
|
||||
import com.bitwarden.network.api.CiphersApi
|
||||
import com.bitwarden.network.base.BaseServiceTest
|
||||
import com.bitwarden.network.model.ArchiveCipherResponseJson
|
||||
import com.bitwarden.network.model.AttachmentJsonResponse
|
||||
import com.bitwarden.network.model.BulkShareCiphersJsonRequest
|
||||
import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest
|
||||
@@ -12,6 +13,7 @@ import com.bitwarden.network.model.FileUploadType
|
||||
import com.bitwarden.network.model.ImportCiphersJsonRequest
|
||||
import com.bitwarden.network.model.ImportCiphersResponseJson
|
||||
import com.bitwarden.network.model.ShareCipherJsonRequest
|
||||
import com.bitwarden.network.model.UnarchiveCipherResponseJson
|
||||
import com.bitwarden.network.model.UpdateCipherCollectionsJsonRequest
|
||||
import com.bitwarden.network.model.UpdateCipherResponseJson
|
||||
import com.bitwarden.network.model.createMockAttachment
|
||||
@@ -65,20 +67,71 @@ class CiphersServiceTest : BaseServiceTest() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `archiveCipher should execute the archiveCipher API`() = runTest {
|
||||
server.enqueue(MockResponse().setResponseCode(200))
|
||||
val cipherId = "cipherId"
|
||||
val result = ciphersService.archiveCipher(cipherId = cipherId)
|
||||
assertEquals(Unit, result.getOrThrow())
|
||||
}
|
||||
fun `archiveCipher with success response should return a Success with the correct cipher`() =
|
||||
runTest {
|
||||
server.enqueue(
|
||||
MockResponse().setBody(CREATE_RESTORE_UPDATE_CIPHER_SUCCESS_JSON),
|
||||
)
|
||||
val result = ciphersService.archiveCipher(cipherId = "cipherId")
|
||||
assertEquals(
|
||||
ArchiveCipherResponseJson.Success(
|
||||
cipher = createMockCipher(number = 1),
|
||||
),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unarchiveCipher should execute the unarchiveCipher API`() = runTest {
|
||||
server.enqueue(MockResponse().setResponseCode(200))
|
||||
val cipherId = "cipherId"
|
||||
val result = ciphersService.unarchiveCipher(cipherId = cipherId)
|
||||
assertEquals(Unit, result.getOrThrow())
|
||||
}
|
||||
fun `archiveCipher with an invalid response should return an Invalid with the correct data`() =
|
||||
runTest {
|
||||
server.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(400)
|
||||
.setBody(UPDATE_CIPHER_INVALID_JSON),
|
||||
)
|
||||
val result = ciphersService.archiveCipher(cipherId = "cipherId")
|
||||
assertEquals(
|
||||
ArchiveCipherResponseJson.Invalid(
|
||||
message = "You do not have permission to edit this.",
|
||||
validationErrors = null,
|
||||
),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `unarchiveCipher with success response should return a Success with the correct cipher`() =
|
||||
runTest {
|
||||
server.enqueue(
|
||||
MockResponse().setBody(CREATE_RESTORE_UPDATE_CIPHER_SUCCESS_JSON),
|
||||
)
|
||||
val result = ciphersService.unarchiveCipher(cipherId = "cipherId")
|
||||
assertEquals(
|
||||
UnarchiveCipherResponseJson.Success(
|
||||
cipher = createMockCipher(number = 1),
|
||||
),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MaxLineLength")
|
||||
@Test
|
||||
fun `unarchiveCipher with an invalid response should return an Invalid with the correct data`() =
|
||||
runTest {
|
||||
server.enqueue(
|
||||
MockResponse()
|
||||
.setResponseCode(400)
|
||||
.setBody(UPDATE_CIPHER_INVALID_JSON),
|
||||
)
|
||||
val result = ciphersService.unarchiveCipher(cipherId = "cipherId")
|
||||
assertEquals(
|
||||
UnarchiveCipherResponseJson.Invalid(
|
||||
message = "You do not have permission to edit this.",
|
||||
validationErrors = null,
|
||||
),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createCipher should return the correct response`() = runTest {
|
||||
|
||||
Reference in New Issue
Block a user