From 07a40c2c049e766933a1cf388f82550acaebe339 Mon Sep 17 00:00:00 2001 From: David Perez Date: Tue, 12 May 2026 11:57:57 -0500 Subject: [PATCH] Chore: Remove Retrofit dependency from app module (#6896) --- app/build.gradle.kts | 2 -- .../data/vault/manager/CipherManagerImpl.kt | 24 +++++++++++------ .../data/vault/manager/SendManagerImpl.kt | 23 ++++++++++------ .../data/vault/manager/CipherManagerTest.kt | 27 ++++++++++--------- .../data/vault/manager/SendManagerTest.kt | 27 ++++++++++--------- .../network/model/GetCipherResponse.kt | 20 ++++++++++++++ .../network/model/GetSendResponse.kt | 20 ++++++++++++++ .../network/service/CiphersService.kt | 3 ++- .../network/service/CiphersServiceImpl.kt | 13 ++++++++- .../network/service/IdentityServiceImpl.kt | 1 + .../bitwarden/network/service/SendsService.kt | 7 ++--- .../network/service/SendsServiceImpl.kt | 13 ++++++++- .../network/util/ExceptionExtensions.kt | 7 +++++ .../network/util/NetworkErrorCode.kt | 1 + .../network/service/CiphersServiceTest.kt | 13 +++++++-- .../network/service/SendsServiceTest.kt | 13 +++++++-- 16 files changed, 161 insertions(+), 53 deletions(-) create mode 100644 network/src/main/kotlin/com/bitwarden/network/model/GetCipherResponse.kt create mode 100644 network/src/main/kotlin/com/bitwarden/network/model/GetSendResponse.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 923d29a541..9364dbc4b5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -285,8 +285,6 @@ dependencies { implementation(libs.kotlinx.serialization) implementation(platform(libs.square.okhttp.bom)) implementation(libs.square.okhttp) - implementation(platform(libs.square.retrofit.bom)) - implementation(libs.square.retrofit) implementation(libs.timber) // For now we are restricted to running Compose tests for debug builds only diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerImpl.kt index d2dbacbcd2..c9e4b6e849 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerImpl.kt @@ -12,6 +12,7 @@ import com.bitwarden.network.model.ArchiveCipherResponseJson import com.bitwarden.network.model.AttachmentJsonResponse import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest import com.bitwarden.network.model.CreateCipherResponseJson +import com.bitwarden.network.model.GetCipherResponse import com.bitwarden.network.model.ShareCipherJsonRequest import com.bitwarden.network.model.UnarchiveCipherResponseJson import com.bitwarden.network.model.UpdateCipherCollectionsJsonRequest @@ -48,7 +49,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import retrofit2.HttpException import java.io.File import java.time.Clock @@ -763,6 +763,7 @@ class CipherManagerImpl( * are met. If the resource cannot be found cloud-side, and it was updated, delete it from disk * for now. */ + @Suppress("CyclomaticComplexMethod") private suspend fun syncCipherIfNecessary(syncCipherUpsertData: SyncCipherUpsertData) { val userId = syncCipherUpsertData.userId val cipherId = syncCipherUpsertData.cipherId @@ -817,15 +818,22 @@ class CipherManagerImpl( ciphersService .getCipher(cipherId = cipherId) .fold( - onSuccess = { vaultDiskSource.saveCipher(userId = userId, cipher = it) }, - onFailure = { - // Delete any updates if it's missing from the server - val httpException = it as? HttpException - @Suppress("MagicNumber") - if (httpException?.code() == 404 && isUpdate) { - vaultDiskSource.deleteCipher(userId = userId, cipherId = cipherId) + onSuccess = { response -> + when (response) { + is GetCipherResponse.NotFound -> { + if (isUpdate) { + vaultDiskSource.deleteCipher(userId = userId, cipherId = cipherId) + } + } + + is GetCipherResponse.Success -> { + vaultDiskSource.saveCipher(userId = userId, cipher = response.cipher) + } } }, + onFailure = { + // no-op + }, ) } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerImpl.kt index 91b09c3b3d..0fa580367e 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerImpl.kt @@ -8,6 +8,7 @@ import com.bitwarden.core.data.util.flatMap import com.bitwarden.data.manager.file.FileManager import com.bitwarden.network.model.CreateFileSendResponse import com.bitwarden.network.model.CreateSendJsonResponse +import com.bitwarden.network.model.GetSendResponse import com.bitwarden.network.model.UpdateSendResponseJson import com.bitwarden.network.service.SendsService import com.bitwarden.send.Send @@ -32,7 +33,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import retrofit2.HttpException /** * The default implementation of the [SendManager]. @@ -291,15 +291,22 @@ class SendManagerImpl( sendsService .getSend(sendId = sendId) .fold( - onSuccess = { vaultDiskSource.saveSend(userId = userId, send = it) }, - onFailure = { - // Delete any updates if it's missing from the server - val httpException = it as? HttpException - @Suppress("MagicNumber") - if (httpException?.code() == 404 && isUpdate) { - vaultDiskSource.deleteSend(userId = userId, sendId = sendId) + onSuccess = { response -> + when (response) { + is GetSendResponse.NotFound -> { + if (isUpdate) { + vaultDiskSource.deleteSend(userId = userId, sendId = sendId) + } + } + + is GetSendResponse.Success -> { + vaultDiskSource.saveSend(userId = userId, send = response.send) + } } }, + onFailure = { + // no-op + }, ) } } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt index 6ac5e5a748..9191c5429b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/CipherManagerTest.kt @@ -13,6 +13,7 @@ import com.bitwarden.network.model.ArchiveCipherResponseJson import com.bitwarden.network.model.AttachmentJsonRequest import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest import com.bitwarden.network.model.CreateCipherResponseJson +import com.bitwarden.network.model.GetCipherResponse import com.bitwarden.network.model.ShareCipherJsonRequest import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.UnarchiveCipherResponseJson @@ -80,7 +81,6 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import retrofit2.HttpException import java.io.File import java.time.Clock import java.time.Instant @@ -2610,6 +2610,7 @@ class CipherManagerTest { revisionDate = clock.instant().minus(5, ChronoUnit.MINUTES), ) val updatedCipher = mockk() + val updatedCipherResponse = GetCipherResponse.Success(cipher = updatedCipher) fakeAuthDiskSource.userState = MOCK_USER_STATE coEvery { @@ -2620,7 +2621,7 @@ class CipherManagerTest { } returns MutableStateFlow(listOf(collection)) coEvery { ciphersService.getCipher(cipherId = cipherId) - } returns updatedCipher.asSuccess() + } returns updatedCipherResponse.asSuccess() coEvery { vaultDiskSource.saveCipher(userId = userId, cipher = updatedCipher) } just runs @@ -2655,6 +2656,7 @@ class CipherManagerTest { revisionDate = clock.instant().minus(5, ChronoUnit.MINUTES), ) val updatedCipher = mockk() + val updatedCipherResponse = GetCipherResponse.Success(cipher = updatedCipher) val collection = createMockCollection(number = number) fakeAuthDiskSource.userState = MOCK_USER_STATE @@ -2663,7 +2665,7 @@ class CipherManagerTest { vaultDiskSource.getCollectionsFlow(userId = userId) } returns MutableStateFlow(listOf(collection)) - coEvery { ciphersService.getCipher(cipherId) } returns updatedCipher.asSuccess() + coEvery { ciphersService.getCipher(cipherId) } returns updatedCipherResponse.asSuccess() coEvery { vaultDiskSource.saveCipher(userId = userId, cipher = updatedCipher) } just runs @@ -2761,10 +2763,9 @@ class CipherManagerTest { coEvery { vaultDiskSource.getCipher(userId = userId, cipherId = cipherId) } returns createMockCipher(number = number) - val response: HttpException = mockk { - every { code() } returns 404 - } - coEvery { ciphersService.getCipher(cipherId = cipherId) } returns response.asFailure() + val response = GetCipherResponse.NotFound(throwable = Throwable("Fail")) + + coEvery { ciphersService.getCipher(cipherId = cipherId) } returns response.asSuccess() coEvery { vaultDiskSource.deleteCipher(userId = userId, cipherId = cipherId) } just runs fakeAuthDiskSource.userState = MOCK_USER_STATE @@ -2794,10 +2795,8 @@ class CipherManagerTest { val cipherId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE - val response: HttpException = mockk { - every { code() } returns 404 - } - coEvery { ciphersService.getCipher(cipherId = cipherId) } returns response.asFailure() + val response = GetCipherResponse.NotFound(throwable = Throwable("Fail")) + coEvery { ciphersService.getCipher(cipherId = cipherId) } returns response.asSuccess() coEvery { vaultDiskSource.getCipher(userId = userId, cipherId = cipherId) } returns null mutableSyncCipherUpsertFlow.tryEmit( @@ -2828,6 +2827,7 @@ class CipherManagerTest { val userId = MOCK_USER_STATE.activeUserId val cipherId = "mockId-$number" val updatedCipher = mockk() + val updatedCipherResponse = GetCipherResponse.Success(cipher = updatedCipher) fakeAuthDiskSource.userState = MOCK_USER_STATE coEvery { @@ -2835,7 +2835,7 @@ class CipherManagerTest { } returns null coEvery { ciphersService.getCipher(cipherId = cipherId) - } returns updatedCipher.asSuccess() + } returns updatedCipherResponse.asSuccess() coEvery { vaultDiskSource.saveCipher(userId = userId, cipher = updatedCipher) } just runs @@ -2869,12 +2869,13 @@ class CipherManagerTest { every { revisionDate } returns clock.instant().minus(5, ChronoUnit.MINUTES) } val updatedCipher = mockk() + val updatedCipherResponse = GetCipherResponse.Success(cipher = updatedCipher) fakeAuthDiskSource.userState = MOCK_USER_STATE coEvery { vaultDiskSource.getCipher(userId = userId, cipherId = cipherId) } returns originalCipher - coEvery { ciphersService.getCipher(cipherId) } returns updatedCipher.asSuccess() + coEvery { ciphersService.getCipher(cipherId) } returns updatedCipherResponse.asSuccess() coEvery { vaultDiskSource.saveCipher(userId = userId, cipher = updatedCipher) } just runs diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt index 8f1ed3a48f..9cb821cb1f 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/SendManagerTest.kt @@ -10,6 +10,7 @@ import com.bitwarden.data.manager.file.FileManager import com.bitwarden.network.exception.CookieRedirectException import com.bitwarden.network.model.CreateFileSendResponse import com.bitwarden.network.model.CreateSendJsonResponse +import com.bitwarden.network.model.GetSendResponse import com.bitwarden.network.model.SendTypeJson import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.UpdateSendResponseJson @@ -53,7 +54,6 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import retrofit2.HttpException import java.io.File import java.time.Clock import java.time.Instant @@ -186,10 +186,11 @@ class SendManagerTest { revisionDate = FIXED_CLOCK.instant(), ) val updatedSend = createMockSend(number = number) + val updatedSendResponse = GetSendResponse.Success(send = updatedSend) coEvery { vaultDiskSource.getSendsFlow(userId = userId) } returns MutableStateFlow(listOf(send)) - coEvery { sendsService.getSend(sendId = sendId) } returns updatedSend.asSuccess() + coEvery { sendsService.getSend(sendId = sendId) } returns updatedSendResponse.asSuccess() coEvery { vaultDiskSource.saveSend(userId = userId, send = updatedSend) } just runs mutableSyncSendUpsertFlow.tryEmit( @@ -216,10 +217,8 @@ class SendManagerTest { val sendId = "mockId-$number" fakeAuthDiskSource.userState = MOCK_USER_STATE - val response: HttpException = mockk { - every { code() } returns 404 - } - coEvery { sendsService.getSend(sendId = sendId) } returns response.asFailure() + val response = GetSendResponse.NotFound(throwable = Throwable("Fail")) + coEvery { sendsService.getSend(sendId = sendId) } returns response.asSuccess() coEvery { vaultDiskSource.deleteSend(userId = userId, sendId = sendId) } just runs @@ -255,10 +254,8 @@ class SendManagerTest { val sendId = "mockId-1" fakeAuthDiskSource.userState = MOCK_USER_STATE - val response: HttpException = mockk { - every { code() } returns 404 - } - coEvery { sendsService.getSend(sendId = sendId) } returns response.asFailure() + val response = GetSendResponse.NotFound(throwable = Throwable("Fail")) + coEvery { sendsService.getSend(sendId = sendId) } returns response.asSuccess() coEvery { vaultDiskSource.getSendsFlow(userId = userId) } returns MutableStateFlow(emptyList()) @@ -293,7 +290,10 @@ class SendManagerTest { vaultDiskSource.getSendsFlow(userId = userId) } returns MutableStateFlow(emptyList()) val send = mockk() - coEvery { sendsService.getSend(sendId = sendId) } returns send.asSuccess() + val updatedSendResponse = GetSendResponse.Success(send = send) + coEvery { + sendsService.getSend(sendId = sendId) + } returns updatedSendResponse.asSuccess() coEvery { vaultDiskSource.saveSend(userId = userId, send = send) } just runs mutableSyncSendUpsertFlow.tryEmit( @@ -329,7 +329,10 @@ class SendManagerTest { } returns MutableStateFlow(listOf(sendView)) val send = mockk() - coEvery { sendsService.getSend(sendId = sendId) } returns send.asSuccess() + val updatedSendResponse = GetSendResponse.Success(send = send) + coEvery { + sendsService.getSend(sendId = sendId) + } returns updatedSendResponse.asSuccess() coEvery { vaultDiskSource.saveSend(userId = userId, send = send) } just runs mutableSyncSendUpsertFlow.tryEmit( diff --git a/network/src/main/kotlin/com/bitwarden/network/model/GetCipherResponse.kt b/network/src/main/kotlin/com/bitwarden/network/model/GetCipherResponse.kt new file mode 100644 index 0000000000..f372e969d0 --- /dev/null +++ b/network/src/main/kotlin/com/bitwarden/network/model/GetCipherResponse.kt @@ -0,0 +1,20 @@ +package com.bitwarden.network.model + +/** + * Models response body from fetching a Cipher. + */ +sealed class GetCipherResponse { + /** + * Models response of a successful Get Cipher request. + */ + data class Success( + val cipher: SyncResponseJson.Cipher, + ) : GetCipherResponse() + + /** + * Models the response of an unfindable Get Cipher request. + */ + data class NotFound( + val throwable: Throwable, + ) : GetCipherResponse() +} diff --git a/network/src/main/kotlin/com/bitwarden/network/model/GetSendResponse.kt b/network/src/main/kotlin/com/bitwarden/network/model/GetSendResponse.kt new file mode 100644 index 0000000000..30296b80ae --- /dev/null +++ b/network/src/main/kotlin/com/bitwarden/network/model/GetSendResponse.kt @@ -0,0 +1,20 @@ +package com.bitwarden.network.model + +/** + * Models response body from fetching a Send. + */ +sealed class GetSendResponse { + /** + * Models response of a successful Get Send request. + */ + data class Success( + val send: SyncResponseJson.Send, + ) : GetSendResponse() + + /** + * Models the response of an unfindable Get Send request. + */ + data class NotFound( + val throwable: Throwable, + ) : GetSendResponse() +} diff --git a/network/src/main/kotlin/com/bitwarden/network/service/CiphersService.kt b/network/src/main/kotlin/com/bitwarden/network/service/CiphersService.kt index 8dabe8a5a3..80d1aca687 100644 --- a/network/src/main/kotlin/com/bitwarden/network/service/CiphersService.kt +++ b/network/src/main/kotlin/com/bitwarden/network/service/CiphersService.kt @@ -9,6 +9,7 @@ import com.bitwarden.network.model.CipherJsonRequest import com.bitwarden.network.model.CipherMiniResponseJson import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest import com.bitwarden.network.model.CreateCipherResponseJson +import com.bitwarden.network.model.GetCipherResponse import com.bitwarden.network.model.ImportCiphersJsonRequest import com.bitwarden.network.model.ImportCiphersResponseJson import com.bitwarden.network.model.ShareCipherJsonRequest @@ -128,7 +129,7 @@ interface CiphersService { /** * Attempt to retrieve a cipher. */ - suspend fun getCipher(cipherId: String): Result + suspend fun getCipher(cipherId: String): Result /** * Attempt to retrieve a cipher's attachment data. diff --git a/network/src/main/kotlin/com/bitwarden/network/service/CiphersServiceImpl.kt b/network/src/main/kotlin/com/bitwarden/network/service/CiphersServiceImpl.kt index 1c57429be2..4c0d3407a3 100644 --- a/network/src/main/kotlin/com/bitwarden/network/service/CiphersServiceImpl.kt +++ b/network/src/main/kotlin/com/bitwarden/network/service/CiphersServiceImpl.kt @@ -13,6 +13,7 @@ import com.bitwarden.network.model.CipherMiniResponseJson import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest import com.bitwarden.network.model.CreateCipherResponseJson import com.bitwarden.network.model.FileUploadType +import com.bitwarden.network.model.GetCipherResponse import com.bitwarden.network.model.ImportCiphersJsonRequest import com.bitwarden.network.model.ImportCiphersResponseJson import com.bitwarden.network.model.ShareCipherJsonRequest @@ -22,6 +23,7 @@ import com.bitwarden.network.model.UpdateCipherCollectionsJsonRequest import com.bitwarden.network.model.UpdateCipherResponseJson import com.bitwarden.network.model.toBitwardenError import com.bitwarden.network.util.NetworkErrorCode +import com.bitwarden.network.util.isErrorCode import com.bitwarden.network.util.parseErrorBodyOrNull import com.bitwarden.network.util.toResult import kotlinx.serialization.json.Json @@ -269,10 +271,19 @@ internal class CiphersServiceImpl( override suspend fun getCipher( cipherId: String, - ): Result = + ): Result = ciphersApi .getCipher(cipherId = cipherId) .toResult() + .map { GetCipherResponse.Success(cipher = it) } + .recoverCatching { throwable -> + val bitwardenError = throwable.toBitwardenError() + if (bitwardenError.isErrorCode(code = NetworkErrorCode.NOT_FOUND)) { + GetCipherResponse.NotFound(throwable = throwable) + } else { + throw throwable + } + } override suspend fun getCipherAttachment( cipherId: String, diff --git a/network/src/main/kotlin/com/bitwarden/network/service/IdentityServiceImpl.kt b/network/src/main/kotlin/com/bitwarden/network/service/IdentityServiceImpl.kt index 85f623da67..131b0f4c66 100644 --- a/network/src/main/kotlin/com/bitwarden/network/service/IdentityServiceImpl.kt +++ b/network/src/main/kotlin/com/bitwarden/network/service/IdentityServiceImpl.kt @@ -121,6 +121,7 @@ internal class IdentityServiceImpl( } NetworkErrorCode.BAD_REQUEST, + NetworkErrorCode.NOT_FOUND, NetworkErrorCode.TOO_MANY_REQUESTS, null, -> throw throwable diff --git a/network/src/main/kotlin/com/bitwarden/network/service/SendsService.kt b/network/src/main/kotlin/com/bitwarden/network/service/SendsService.kt index e5af889cc4..17b59a07ce 100644 --- a/network/src/main/kotlin/com/bitwarden/network/service/SendsService.kt +++ b/network/src/main/kotlin/com/bitwarden/network/service/SendsService.kt @@ -3,6 +3,7 @@ package com.bitwarden.network.service import com.bitwarden.network.model.CreateFileSendResponse import com.bitwarden.network.model.CreateFileSendResponseJson import com.bitwarden.network.model.CreateSendJsonResponse +import com.bitwarden.network.model.GetSendResponse import com.bitwarden.network.model.SendJsonRequest import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.UpdateSendResponseJson @@ -43,7 +44,7 @@ interface SendsService { ): Result /** - * Attempt to delete a send. + * Attempt to delete a Send. */ suspend fun deleteSend( sendId: String, @@ -57,7 +58,7 @@ interface SendsService { ): Result /** - * Attempt to retrieve a send. + * Attempt to retrieve a Send. */ - suspend fun getSend(sendId: String): Result + suspend fun getSend(sendId: String): Result } diff --git a/network/src/main/kotlin/com/bitwarden/network/service/SendsServiceImpl.kt b/network/src/main/kotlin/com/bitwarden/network/service/SendsServiceImpl.kt index 22bb3d3484..ec4c78d027 100644 --- a/network/src/main/kotlin/com/bitwarden/network/service/SendsServiceImpl.kt +++ b/network/src/main/kotlin/com/bitwarden/network/service/SendsServiceImpl.kt @@ -7,11 +7,13 @@ import com.bitwarden.network.model.CreateFileSendResponse import com.bitwarden.network.model.CreateFileSendResponseJson import com.bitwarden.network.model.CreateSendJsonResponse import com.bitwarden.network.model.FileUploadType +import com.bitwarden.network.model.GetSendResponse import com.bitwarden.network.model.SendJsonRequest import com.bitwarden.network.model.SyncResponseJson import com.bitwarden.network.model.UpdateSendResponseJson import com.bitwarden.network.model.toBitwardenError import com.bitwarden.network.util.NetworkErrorCode +import com.bitwarden.network.util.isErrorCode import com.bitwarden.network.util.parseErrorBodyOrNull import com.bitwarden.network.util.toResult import kotlinx.serialization.json.Json @@ -152,8 +154,17 @@ internal class SendsServiceImpl( override suspend fun getSend( sendId: String, - ): Result = + ): Result = sendsApi .getSend(sendId = sendId) .toResult() + .map { GetSendResponse.Success(send = it) } + .recoverCatching { throwable -> + val bitwardenError = throwable.toBitwardenError() + if (bitwardenError.isErrorCode(code = NetworkErrorCode.NOT_FOUND)) { + GetSendResponse.NotFound(throwable = throwable) + } else { + throw throwable + } + } } diff --git a/network/src/main/kotlin/com/bitwarden/network/util/ExceptionExtensions.kt b/network/src/main/kotlin/com/bitwarden/network/util/ExceptionExtensions.kt index 6cda1f1d60..b1a404c4c7 100644 --- a/network/src/main/kotlin/com/bitwarden/network/util/ExceptionExtensions.kt +++ b/network/src/main/kotlin/com/bitwarden/network/util/ExceptionExtensions.kt @@ -13,6 +13,13 @@ internal fun BitwardenError.getNetworkErrorCodeOrNull(): NetworkErrorCode? = NetworkErrorCode.entries.firstOrNull { httpError.code == it.code } } +/** + * Returns the `true` if the underlying error is from the [code] provided. + */ +internal fun BitwardenError.isErrorCode( + code: NetworkErrorCode, +): Boolean = (this as? BitwardenError.Http)?.let { code.code == this.code } == true + /** * Attempt to parse the error body to serializable type [T]. * diff --git a/network/src/main/kotlin/com/bitwarden/network/util/NetworkErrorCode.kt b/network/src/main/kotlin/com/bitwarden/network/util/NetworkErrorCode.kt index e9064ea3ad..efad7ca394 100644 --- a/network/src/main/kotlin/com/bitwarden/network/util/NetworkErrorCode.kt +++ b/network/src/main/kotlin/com/bitwarden/network/util/NetworkErrorCode.kt @@ -9,5 +9,6 @@ internal enum class NetworkErrorCode( BAD_REQUEST(code = 400), UNAUTHORIZED(code = 401), FORBIDDEN(code = 403), + NOT_FOUND(code = 404), TOO_MANY_REQUESTS(code = 429), } diff --git a/network/src/test/kotlin/com/bitwarden/network/service/CiphersServiceTest.kt b/network/src/test/kotlin/com/bitwarden/network/service/CiphersServiceTest.kt index 89b4f5da37..1c68399eed 100644 --- a/network/src/test/kotlin/com/bitwarden/network/service/CiphersServiceTest.kt +++ b/network/src/test/kotlin/com/bitwarden/network/service/CiphersServiceTest.kt @@ -10,6 +10,7 @@ import com.bitwarden.network.model.BulkShareCiphersJsonRequest import com.bitwarden.network.model.CreateCipherInOrganizationJsonRequest import com.bitwarden.network.model.CreateCipherResponseJson import com.bitwarden.network.model.FileUploadType +import com.bitwarden.network.model.GetCipherResponse import com.bitwarden.network.model.ImportCiphersJsonRequest import com.bitwarden.network.model.ImportCiphersResponseJson import com.bitwarden.network.model.ShareCipherJsonRequest @@ -35,6 +36,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertInstanceOf import retrofit2.create import java.io.File import java.time.Clock @@ -440,15 +442,22 @@ class CiphersServiceTest : BaseServiceTest() { } @Test - fun `getCipher should return the correct response`() = runTest { + fun `getCipher with success should return the correct response`() = runTest { server.enqueue(MockResponse().setBody(CREATE_RESTORE_UPDATE_CIPHER_SUCCESS_JSON)) val result = ciphersService.getCipher(cipherId = "mockId-1") assertEquals( - createMockCipher(number = 1), + GetCipherResponse.Success(cipher = createMockCipher(number = 1)), result.getOrThrow(), ) } + @Test + fun `getSend with 404 should return the NotFound response`() = runTest { + server.enqueue(MockResponse().setResponseCode(404)) + val result = ciphersService.getCipher("mockId-1") + assertInstanceOf(result.getOrThrow()) + } + @Test fun `getCipherAttachment should return the correct response`() = runTest { server.enqueue(MockResponse().setBody(GET_CIPHER_ATTACHMENT_SUCCESS_JSON)) diff --git a/network/src/test/kotlin/com/bitwarden/network/service/SendsServiceTest.kt b/network/src/test/kotlin/com/bitwarden/network/service/SendsServiceTest.kt index 5292f3346a..26392a3a76 100644 --- a/network/src/test/kotlin/com/bitwarden/network/service/SendsServiceTest.kt +++ b/network/src/test/kotlin/com/bitwarden/network/service/SendsServiceTest.kt @@ -6,6 +6,7 @@ import com.bitwarden.network.api.SendsApi import com.bitwarden.network.base.BaseServiceTest import com.bitwarden.network.model.CreateFileSendResponse import com.bitwarden.network.model.CreateSendJsonResponse +import com.bitwarden.network.model.GetSendResponse import com.bitwarden.network.model.SendTypeJson import com.bitwarden.network.model.UpdateSendResponseJson import com.bitwarden.network.model.createMockFileSendResponseJson @@ -21,6 +22,7 @@ import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertInstanceOf import retrofit2.create import java.io.File import java.time.Clock @@ -180,11 +182,18 @@ class SendsServiceTest : BaseServiceTest() { } @Test - fun `getSend should return the correct response`() = runTest { + fun `getSend with success should return the success response`() = runTest { val response = createMockSend(number = 1) server.enqueue(MockResponse().setBody(CREATE_UPDATE_SEND_SUCCESS_JSON)) val result = sendsService.getSend("mockId-1") - assertEquals(response, result.getOrThrow()) + assertEquals(GetSendResponse.Success(send = response), result.getOrThrow()) + } + + @Test + fun `getSend with 404 should return the NotFound response`() = runTest { + server.enqueue(MockResponse().setResponseCode(404)) + val result = sendsService.getSend("mockId-1") + assertInstanceOf(result.getOrThrow()) } private fun setupMockUri(