mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
BIT-2129 Show descriptive error message when Send creation fails (#1186)
This commit is contained in:
parent
90ff2897f5
commit
1150f01666
14 changed files with 277 additions and 101 deletions
|
@ -1,7 +1,7 @@
|
||||||
package com.x8bit.bitwarden.data.vault.datasource.network.api
|
package com.x8bit.bitwarden.data.vault.datasource.network.api
|
||||||
|
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendFileResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponseJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
|
@ -28,7 +28,7 @@ interface SendsApi {
|
||||||
* Create a file send.
|
* Create a file send.
|
||||||
*/
|
*/
|
||||||
@POST("sends/file/v2")
|
@POST("sends/file/v2")
|
||||||
suspend fun createFileSend(@Body body: SendJsonRequest): Result<SendFileResponseJson>
|
suspend fun createFileSend(@Body body: SendJsonRequest): Result<CreateFileSendResponseJson>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a send.
|
* Updates a send.
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a response from create file send.
|
||||||
|
*/
|
||||||
|
sealed class CreateFileSendResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the response from a successful create file send request.
|
||||||
|
*
|
||||||
|
* @property createFileJsonResponse Response JSON received from create file send request.
|
||||||
|
*/
|
||||||
|
data class Success(
|
||||||
|
val createFileJsonResponse: CreateFileSendResponseJson,
|
||||||
|
) : CreateFileSendResponse()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the json body of an invalid create request.
|
||||||
|
*
|
||||||
|
* @property message A general, user-displayable error message.
|
||||||
|
* @property 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>>?,
|
||||||
|
) : CreateFileSendResponse(), InvalidJsonResponse
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable
|
||||||
* Represents the JSON response from creating a new file send.
|
* Represents the JSON response from creating a new file send.
|
||||||
*/
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
data class SendFileResponseJson(
|
data class CreateFileSendResponseJson(
|
||||||
@SerialName("url")
|
@SerialName("url")
|
||||||
val url: String,
|
val url: String,
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Models a response from either "create text send" or "create file send" requests.
|
||||||
|
*/
|
||||||
|
sealed class CreateSendJsonResponse {
|
||||||
|
/**
|
||||||
|
* Represents a successful response from either "create text send" or "create file send"
|
||||||
|
* request.
|
||||||
|
*
|
||||||
|
* @property send The created send object.
|
||||||
|
*/
|
||||||
|
data class Success(val send: SyncResponseJson.Send) : CreateSendJsonResponse()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the json body of an invalid create request.
|
||||||
|
*
|
||||||
|
* @property message A general, user-displayable error message.
|
||||||
|
* @property 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>>?,
|
||||||
|
) : CreateSendJsonResponse(), InvalidJsonResponse
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the json body of an invalid send json request.
|
||||||
|
*/
|
||||||
|
sealed interface InvalidJsonResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A general, user-displayable error message.
|
||||||
|
*/
|
||||||
|
val message: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)
|
||||||
|
*/
|
||||||
|
val validationErrors: Map<String, List<String>>?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first error message found in [validationErrors], or [message] if there are no
|
||||||
|
* [validationErrors] present.
|
||||||
|
*/
|
||||||
|
val firstValidationErrorMessage: String?
|
||||||
|
get() = validationErrors
|
||||||
|
?.flatMap { it.value }
|
||||||
|
?.first()
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
package com.x8bit.bitwarden.data.vault.datasource.network.service
|
package com.x8bit.bitwarden.data.vault.datasource.network.service
|
||||||
|
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendFileResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponse
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponseJson
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateSendJsonResponse
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateSendResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateSendResponseJson
|
||||||
|
@ -14,20 +16,20 @@ interface SendsService {
|
||||||
*/
|
*/
|
||||||
suspend fun createTextSend(
|
suspend fun createTextSend(
|
||||||
body: SendJsonRequest,
|
body: SendJsonRequest,
|
||||||
): Result<SyncResponseJson.Send>
|
): Result<CreateSendJsonResponse>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to create a file send.
|
* Attempt to create a file send.
|
||||||
*/
|
*/
|
||||||
suspend fun createFileSend(
|
suspend fun createFileSend(
|
||||||
body: SendJsonRequest,
|
body: SendJsonRequest,
|
||||||
): Result<SendFileResponseJson>
|
): Result<CreateFileSendResponse>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to upload the given [encryptedFile] associated with the [sendFileResponse].
|
* Attempt to upload the given [encryptedFile] associated with the [sendFileResponse].
|
||||||
*/
|
*/
|
||||||
suspend fun uploadFile(
|
suspend fun uploadFile(
|
||||||
sendFileResponse: SendFileResponseJson,
|
sendFileResponse: CreateFileSendResponseJson,
|
||||||
encryptedFile: ByteArray,
|
encryptedFile: ByteArray,
|
||||||
): Result<SyncResponseJson.Send>
|
): Result<SyncResponseJson.Send>
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,10 @@ import com.x8bit.bitwarden.data.platform.datasource.network.model.toBitwardenErr
|
||||||
import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull
|
import com.x8bit.bitwarden.data.platform.datasource.network.util.parseErrorBodyOrNull
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.api.AzureApi
|
import com.x8bit.bitwarden.data.vault.datasource.network.api.AzureApi
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.api.SendsApi
|
import com.x8bit.bitwarden.data.vault.datasource.network.api.SendsApi
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponse
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponseJson
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateSendJsonResponse
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FileUploadType
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.FileUploadType
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendFileResponseJson
|
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateSendResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateSendResponseJson
|
||||||
|
@ -28,11 +30,33 @@ class SendsServiceImpl(
|
||||||
private val clock: Clock,
|
private val clock: Clock,
|
||||||
private val json: Json,
|
private val json: Json,
|
||||||
) : SendsService {
|
) : SendsService {
|
||||||
override suspend fun createTextSend(body: SendJsonRequest): Result<SyncResponseJson.Send> =
|
override suspend fun createTextSend(
|
||||||
|
body: SendJsonRequest,
|
||||||
|
): Result<CreateSendJsonResponse> =
|
||||||
sendsApi.createTextSend(body = body)
|
sendsApi.createTextSend(body = body)
|
||||||
|
.map { CreateSendJsonResponse.Success(send = it) }
|
||||||
|
.recoverCatching { throwable ->
|
||||||
|
throwable.toBitwardenError()
|
||||||
|
.parseErrorBodyOrNull<CreateSendJsonResponse.Invalid>(
|
||||||
|
code = 400,
|
||||||
|
json = json,
|
||||||
|
)
|
||||||
|
?: throw throwable
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun createFileSend(body: SendJsonRequest): Result<SendFileResponseJson> =
|
override suspend fun createFileSend(
|
||||||
|
body: SendJsonRequest,
|
||||||
|
): Result<CreateFileSendResponse> =
|
||||||
sendsApi.createFileSend(body = body)
|
sendsApi.createFileSend(body = body)
|
||||||
|
.map { CreateFileSendResponse.Success(it) }
|
||||||
|
.recoverCatching { throwable ->
|
||||||
|
throwable.toBitwardenError()
|
||||||
|
.parseErrorBodyOrNull<CreateFileSendResponse.Invalid>(
|
||||||
|
code = 400,
|
||||||
|
json = json,
|
||||||
|
)
|
||||||
|
?: throw throwable
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun updateSend(
|
override suspend fun updateSend(
|
||||||
sendId: String,
|
sendId: String,
|
||||||
|
@ -55,7 +79,7 @@ class SendsServiceImpl(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun uploadFile(
|
override suspend fun uploadFile(
|
||||||
sendFileResponse: SendFileResponseJson,
|
sendFileResponse: CreateFileSendResponseJson,
|
||||||
encryptedFile: ByteArray,
|
encryptedFile: ByteArray,
|
||||||
): Result<SyncResponseJson.Send> {
|
): Result<SyncResponseJson.Send> {
|
||||||
val send = sendFileResponse.sendResponse
|
val send = sendFileResponse.sendResponse
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.bitwarden.core.ExportFormat
|
||||||
import com.bitwarden.core.FolderView
|
import com.bitwarden.core.FolderView
|
||||||
import com.bitwarden.core.InitOrgCryptoRequest
|
import com.bitwarden.core.InitOrgCryptoRequest
|
||||||
import com.bitwarden.core.InitUserCryptoMethod
|
import com.bitwarden.core.InitUserCryptoMethod
|
||||||
|
import com.bitwarden.core.Send
|
||||||
import com.bitwarden.core.SendType
|
import com.bitwarden.core.SendType
|
||||||
import com.bitwarden.core.SendView
|
import com.bitwarden.core.SendView
|
||||||
import com.bitwarden.crypto.Kdf
|
import com.bitwarden.crypto.Kdf
|
||||||
|
@ -35,10 +36,13 @@ import com.x8bit.bitwarden.data.platform.repository.util.map
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.observeWhenSubscribedAndLoggedIn
|
import com.x8bit.bitwarden.data.platform.repository.util.observeWhenSubscribedAndLoggedIn
|
||||||
import com.x8bit.bitwarden.data.platform.repository.util.updateToPendingOrLoading
|
import com.x8bit.bitwarden.data.platform.repository.util.updateToPendingOrLoading
|
||||||
import com.x8bit.bitwarden.data.platform.util.asFailure
|
import com.x8bit.bitwarden.data.platform.util.asFailure
|
||||||
|
import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||||
import com.x8bit.bitwarden.data.platform.util.flatMap
|
import com.x8bit.bitwarden.data.platform.util.flatMap
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateCipherInOrganizationJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateCipherInOrganizationJsonRequest
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponse
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateSendJsonResponse
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.ShareCipherJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.ShareCipherJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateCipherCollectionsJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateCipherCollectionsJsonRequest
|
||||||
|
@ -980,11 +984,12 @@ class VaultRepositoryImpl(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("ReturnCount")
|
||||||
override suspend fun createSend(
|
override suspend fun createSend(
|
||||||
sendView: SendView,
|
sendView: SendView,
|
||||||
fileUri: Uri?,
|
fileUri: Uri?,
|
||||||
): CreateSendResult {
|
): CreateSendResult {
|
||||||
val userId = activeUserId ?: return CreateSendResult.Error
|
val userId = activeUserId ?: return CreateSendResult.Error(message = null)
|
||||||
return vaultSdkSource
|
return vaultSdkSource
|
||||||
.encryptSend(
|
.encryptSend(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
|
@ -992,54 +997,33 @@ class VaultRepositoryImpl(
|
||||||
)
|
)
|
||||||
.flatMap { send ->
|
.flatMap { send ->
|
||||||
when (send.type) {
|
when (send.type) {
|
||||||
SendType.TEXT -> {
|
SendType.TEXT -> sendsService.createTextSend(send.toEncryptedNetworkSend())
|
||||||
sendsService.createTextSend(body = send.toEncryptedNetworkSend())
|
SendType.FILE -> createFileSend(fileUri, userId, send)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.map { createSendResponse ->
|
||||||
|
when (createSendResponse) {
|
||||||
|
is CreateSendJsonResponse.Invalid -> {
|
||||||
|
return CreateSendResult.Error(
|
||||||
|
message = createSendResponse.firstValidationErrorMessage,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
SendType.FILE -> {
|
is CreateSendJsonResponse.Success -> {
|
||||||
val uri = fileUri ?: return@flatMap IllegalArgumentException(
|
|
||||||
"File URI must be present to create a File Send.",
|
|
||||||
)
|
|
||||||
.asFailure()
|
|
||||||
|
|
||||||
fileManager
|
|
||||||
.uriToByteArray(fileUri = uri)
|
|
||||||
.flatMap {
|
|
||||||
vaultSdkSource.encryptBuffer(
|
|
||||||
userId = userId,
|
|
||||||
send = send,
|
|
||||||
fileBuffer = it,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.flatMap { encryptedFile ->
|
|
||||||
sendsService
|
|
||||||
.createFileSend(
|
|
||||||
body = send.toEncryptedNetworkSend(
|
|
||||||
fileLength = encryptedFile.size,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.flatMap { sendFileResponse ->
|
|
||||||
sendsService.uploadFile(
|
|
||||||
sendFileResponse = sendFileResponse,
|
|
||||||
encryptedFile = encryptedFile,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onSuccess {
|
|
||||||
// Save the send immediately, regardless of whether the decrypt succeeds
|
// Save the send immediately, regardless of whether the decrypt succeeds
|
||||||
vaultDiskSource.saveSend(userId = userId, send = it)
|
vaultDiskSource.saveSend(userId = userId, send = createSendResponse.send)
|
||||||
|
createSendResponse
|
||||||
}
|
}
|
||||||
.flatMap {
|
}
|
||||||
|
}
|
||||||
|
.flatMap { createSendSuccessResponse ->
|
||||||
vaultSdkSource.decryptSend(
|
vaultSdkSource.decryptSend(
|
||||||
userId = userId,
|
userId = userId,
|
||||||
send = it.toEncryptedSdkSend(),
|
send = createSendSuccessResponse.send.toEncryptedSdkSend(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.fold(
|
.fold(
|
||||||
onFailure = { CreateSendResult.Error },
|
onFailure = { CreateSendResult.Error(message = null) },
|
||||||
onSuccess = { CreateSendResult.Success(it) },
|
onSuccess = { CreateSendResult.Success(it) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1594,6 +1578,56 @@ class VaultRepositoryImpl(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun createFileSend(
|
||||||
|
uri: Uri?,
|
||||||
|
userId: String,
|
||||||
|
send: Send,
|
||||||
|
): Result<CreateSendJsonResponse> {
|
||||||
|
uri ?: return IllegalArgumentException(
|
||||||
|
"File URI must be present to create a File Send.",
|
||||||
|
)
|
||||||
|
.asFailure()
|
||||||
|
|
||||||
|
return fileManager
|
||||||
|
.uriToByteArray(fileUri = uri)
|
||||||
|
.flatMap {
|
||||||
|
vaultSdkSource.encryptBuffer(
|
||||||
|
userId = userId,
|
||||||
|
send = send,
|
||||||
|
fileBuffer = it,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.flatMap { encryptedFile ->
|
||||||
|
sendsService
|
||||||
|
.createFileSend(
|
||||||
|
body = send.toEncryptedNetworkSend(
|
||||||
|
fileLength = encryptedFile.size,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.flatMap { sendFileResponse ->
|
||||||
|
when (sendFileResponse) {
|
||||||
|
is CreateFileSendResponse.Invalid -> {
|
||||||
|
CreateSendJsonResponse
|
||||||
|
.Invalid(
|
||||||
|
message = sendFileResponse.message,
|
||||||
|
validationErrors = sendFileResponse.validationErrors,
|
||||||
|
)
|
||||||
|
.asSuccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
is CreateFileSendResponse.Success -> {
|
||||||
|
sendsService
|
||||||
|
.uploadFile(
|
||||||
|
sendFileResponse = sendFileResponse.createFileJsonResponse,
|
||||||
|
encryptedFile = encryptedFile,
|
||||||
|
)
|
||||||
|
.map { CreateSendJsonResponse.Success(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the send specified by [syncSendDeleteData] from disk.
|
* Deletes the send specified by [syncSendDeleteData] from disk.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -15,5 +15,5 @@ sealed class CreateSendResult {
|
||||||
/**
|
/**
|
||||||
* Generic error while creating a send.
|
* Generic error while creating a send.
|
||||||
*/
|
*/
|
||||||
data object Error : CreateSendResult()
|
data class Error(val message: String?) : CreateSendResult()
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,12 +184,13 @@ class AddSendViewModel @Inject constructor(
|
||||||
action: AddSendAction.Internal.CreateSendResultReceive,
|
action: AddSendAction.Internal.CreateSendResultReceive,
|
||||||
) {
|
) {
|
||||||
when (val result = action.result) {
|
when (val result = action.result) {
|
||||||
CreateSendResult.Error -> {
|
is CreateSendResult.Error -> {
|
||||||
mutableStateFlow.update {
|
mutableStateFlow.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
dialogState = AddSendState.DialogState.Error(
|
dialogState = AddSendState.DialogState.Error(
|
||||||
title = R.string.an_error_has_occurred.asText(),
|
title = R.string.an_error_has_occurred.asText(),
|
||||||
message = R.string.generic_error_message.asText(),
|
message = result.message?.asText()
|
||||||
|
?: R.string.generic_error_message.asText(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,13 @@ package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||||
|
|
||||||
import java.time.ZonedDateTime
|
import java.time.ZonedDateTime
|
||||||
|
|
||||||
|
fun createMockFileSendResponseJson(number: Int, type: SendTypeJson = SendTypeJson.FILE) =
|
||||||
|
CreateFileSendResponseJson(
|
||||||
|
url = "www.test.com",
|
||||||
|
fileUploadType = FileUploadType.AZURE,
|
||||||
|
sendResponse = createMockSend(number = number, type = type),
|
||||||
|
)
|
||||||
|
|
||||||
fun createMockSend(
|
fun createMockSend(
|
||||||
number: Int,
|
number: Int,
|
||||||
type: SendTypeJson = SendTypeJson.FILE,
|
type: SendTypeJson = SendTypeJson.FILE,
|
||||||
|
|
|
@ -4,10 +4,11 @@ import android.net.Uri
|
||||||
import com.x8bit.bitwarden.data.platform.base.BaseServiceTest
|
import com.x8bit.bitwarden.data.platform.base.BaseServiceTest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.api.AzureApi
|
import com.x8bit.bitwarden.data.vault.datasource.network.api.AzureApi
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.api.SendsApi
|
import com.x8bit.bitwarden.data.vault.datasource.network.api.SendsApi
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FileUploadType
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponse
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendFileResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateSendJsonResponse
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateSendResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateSendResponseJson
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFileSendResponseJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSend
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSend
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSendJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockSendJsonRequest
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
|
@ -52,16 +53,18 @@ class SendsServiceTest : BaseServiceTest() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `createFileSend should return the correct response`() = runTest {
|
fun `createFileSend should return the correct response`() = runTest {
|
||||||
val response = SendFileResponseJson(
|
val sendFileResponse = CreateFileSendResponse.Success(
|
||||||
url = "www.test.com",
|
createFileJsonResponse = createMockFileSendResponseJson(number = 1),
|
||||||
fileUploadType = FileUploadType.AZURE,
|
|
||||||
sendResponse = createMockSend(number = 1, type = SendTypeJson.FILE),
|
|
||||||
)
|
)
|
||||||
server.enqueue(MockResponse().setBody(CREATE_FILE_SEND_SUCCESS_JSON))
|
server.enqueue(MockResponse().setBody(CREATE_FILE_SEND_SUCCESS_JSON))
|
||||||
val result = sendsService.createFileSend(
|
val result = sendsService.createFileSend(
|
||||||
body = createMockSendJsonRequest(number = 1, type = SendTypeJson.FILE),
|
body = createMockSendJsonRequest(number = 1, type = SendTypeJson.FILE),
|
||||||
)
|
)
|
||||||
assertEquals(response, result.getOrThrow())
|
|
||||||
|
assertEquals(
|
||||||
|
sendFileResponse,
|
||||||
|
result.getOrThrow(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -71,7 +74,7 @@ class SendsServiceTest : BaseServiceTest() {
|
||||||
body = createMockSendJsonRequest(number = 1, type = SendTypeJson.TEXT),
|
body = createMockSendJsonRequest(number = 1, type = SendTypeJson.TEXT),
|
||||||
)
|
)
|
||||||
assertEquals(
|
assertEquals(
|
||||||
createMockSend(number = 1),
|
CreateSendJsonResponse.Success(createMockSend(number = 1)),
|
||||||
result.getOrThrow(),
|
result.getOrThrow(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -113,13 +116,9 @@ class SendsServiceTest : BaseServiceTest() {
|
||||||
fun `uploadFile with Azure uploadFile success should return send`() = runTest {
|
fun `uploadFile with Azure uploadFile success should return send`() = runTest {
|
||||||
val url = "www.test.com"
|
val url = "www.test.com"
|
||||||
setupMockUri(url = url, queryParams = mapOf("sv" to "2024-04-03"))
|
setupMockUri(url = url, queryParams = mapOf("sv" to "2024-04-03"))
|
||||||
val mockSend = createMockSend(number = 1, type = SendTypeJson.FILE)
|
val sendFileResponse = createMockFileSendResponseJson(number = 1)
|
||||||
val sendFileResponse = SendFileResponseJson(
|
|
||||||
url = url,
|
|
||||||
fileUploadType = FileUploadType.AZURE,
|
|
||||||
sendResponse = mockSend,
|
|
||||||
)
|
|
||||||
val encryptedFile = byteArrayOf()
|
val encryptedFile = byteArrayOf()
|
||||||
|
|
||||||
server.enqueue(MockResponse().setResponseCode(201))
|
server.enqueue(MockResponse().setResponseCode(201))
|
||||||
|
|
||||||
val result = sendsService.uploadFile(
|
val result = sendsService.uploadFile(
|
||||||
|
@ -127,17 +126,14 @@ class SendsServiceTest : BaseServiceTest() {
|
||||||
encryptedFile = encryptedFile,
|
encryptedFile = encryptedFile,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(mockSend, result.getOrThrow())
|
assertEquals(sendFileResponse.sendResponse, result.getOrThrow())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `uploadFile with Direct uploadFile success should return send`() = runTest {
|
fun `uploadFile with Direct uploadFile success should return send`() = runTest {
|
||||||
val mockSend = createMockSend(number = 1, type = SendTypeJson.FILE)
|
val url = "www.test.com"
|
||||||
val sendFileResponse = SendFileResponseJson(
|
setupMockUri(url = url, queryParams = mapOf("sv" to "2024-04-03"))
|
||||||
url = "www.test.com",
|
val sendFileResponse = createMockFileSendResponseJson(number = 1)
|
||||||
fileUploadType = FileUploadType.DIRECT,
|
|
||||||
sendResponse = mockSend,
|
|
||||||
)
|
|
||||||
val encryptedFile = byteArrayOf()
|
val encryptedFile = byteArrayOf()
|
||||||
server.enqueue(MockResponse().setResponseCode(201))
|
server.enqueue(MockResponse().setResponseCode(201))
|
||||||
|
|
||||||
|
@ -146,7 +142,7 @@ class SendsServiceTest : BaseServiceTest() {
|
||||||
encryptedFile = encryptedFile,
|
encryptedFile = encryptedFile,
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(mockSend, result.getOrThrow())
|
assertEquals(sendFileResponse.sendResponse, result.getOrThrow())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -39,9 +39,9 @@ import com.x8bit.bitwarden.data.platform.util.asSuccess
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.AttachmentJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateCipherInOrganizationJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateCipherInOrganizationJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FileUploadType
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateFileSendResponse
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.CreateSendJsonResponse
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendFileResponseJson
|
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.ShareCipherJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.ShareCipherJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||||
|
@ -55,6 +55,7 @@ import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCipher
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCipherJsonRequest
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCipherJsonRequest
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCollection
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockCollection
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockDomains
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockDomains
|
||||||
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFileSendResponseJson
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockFolder
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganization
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganization
|
||||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganizationKeys
|
import com.x8bit.bitwarden.data.vault.datasource.network.model.createMockOrganizationKeys
|
||||||
|
@ -2430,7 +2431,7 @@ class VaultRepositoryTest {
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
CreateSendResult.Error,
|
CreateSendResult.Error(message = null),
|
||||||
result,
|
result,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2447,7 +2448,7 @@ class VaultRepositoryTest {
|
||||||
|
|
||||||
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
||||||
|
|
||||||
assertEquals(CreateSendResult.Error, result)
|
assertEquals(CreateSendResult.Error(message = null), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2469,7 +2470,7 @@ class VaultRepositoryTest {
|
||||||
|
|
||||||
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
||||||
|
|
||||||
assertEquals(CreateSendResult.Error, result)
|
assertEquals(CreateSendResult.Error(IllegalStateException().message), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
|
@ -2482,6 +2483,7 @@ class VaultRepositoryTest {
|
||||||
val mockSdkSend = createMockSdkSend(number = 1, type = SendType.TEXT)
|
val mockSdkSend = createMockSdkSend(number = 1, type = SendType.TEXT)
|
||||||
val mockSend = createMockSend(number = 1, type = SendTypeJson.TEXT)
|
val mockSend = createMockSend(number = 1, type = SendTypeJson.TEXT)
|
||||||
val mockSendViewResult = createMockSendView(number = 2)
|
val mockSendViewResult = createMockSendView(number = 2)
|
||||||
|
val sendTextResponse = CreateSendJsonResponse.Success(send = mockSend)
|
||||||
coEvery {
|
coEvery {
|
||||||
vaultSdkSource.encryptSend(userId = userId, sendView = mockSendView)
|
vaultSdkSource.encryptSend(userId = userId, sendView = mockSendView)
|
||||||
} returns mockSdkSend.asSuccess()
|
} returns mockSdkSend.asSuccess()
|
||||||
|
@ -2490,7 +2492,7 @@ class VaultRepositoryTest {
|
||||||
body = createMockSendJsonRequest(number = 1, type = SendTypeJson.TEXT)
|
body = createMockSendJsonRequest(number = 1, type = SendTypeJson.TEXT)
|
||||||
.copy(fileLength = null),
|
.copy(fileLength = null),
|
||||||
)
|
)
|
||||||
} returns mockSend.asSuccess()
|
} returns sendTextResponse.asSuccess()
|
||||||
coEvery { vaultDiskSource.saveSend(userId, mockSend) } just runs
|
coEvery { vaultDiskSource.saveSend(userId, mockSend) } just runs
|
||||||
coEvery {
|
coEvery {
|
||||||
vaultSdkSource.decryptSend(userId, mockSdkSend)
|
vaultSdkSource.decryptSend(userId, mockSdkSend)
|
||||||
|
@ -2529,7 +2531,7 @@ class VaultRepositoryTest {
|
||||||
|
|
||||||
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = uri)
|
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = uri)
|
||||||
|
|
||||||
assertEquals(CreateSendResult.Error, result)
|
assertEquals(CreateSendResult.Error(IllegalStateException().message), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2544,14 +2546,18 @@ class VaultRepositoryTest {
|
||||||
val mockSdkSend = createMockSdkSend(number = 1)
|
val mockSdkSend = createMockSdkSend(number = 1)
|
||||||
val byteArray = byteArrayOf(1)
|
val byteArray = byteArrayOf(1)
|
||||||
val encryptedByteArray = byteArrayOf(2)
|
val encryptedByteArray = byteArrayOf(2)
|
||||||
val sendFileResponse = SendFileResponseJson(
|
val sendFileResponse = CreateFileSendResponse.Success(
|
||||||
url = url,
|
createMockFileSendResponseJson(
|
||||||
fileUploadType = FileUploadType.AZURE,
|
number = 1,
|
||||||
sendResponse = createMockSend(number = 1, type = SendTypeJson.FILE),
|
type = SendTypeJson.FILE,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
coEvery {
|
coEvery {
|
||||||
vaultSdkSource.encryptSend(userId = userId, sendView = mockSendView)
|
vaultSdkSource.encryptSend(userId = userId, sendView = mockSendView)
|
||||||
} returns mockSdkSend.asSuccess()
|
} returns mockSdkSend.asSuccess()
|
||||||
|
coEvery {
|
||||||
|
vaultSdkSource.decryptSend(userId, mockSdkSend)
|
||||||
|
} returns mockSendView.asSuccess()
|
||||||
coEvery { fileManager.uriToByteArray(any()) } returns byteArray.asSuccess()
|
coEvery { fileManager.uriToByteArray(any()) } returns byteArray.asSuccess()
|
||||||
coEvery {
|
coEvery {
|
||||||
vaultSdkSource.encryptBuffer(
|
vaultSdkSource.encryptBuffer(
|
||||||
|
@ -2560,19 +2566,25 @@ class VaultRepositoryTest {
|
||||||
fileBuffer = byteArray,
|
fileBuffer = byteArray,
|
||||||
)
|
)
|
||||||
} returns encryptedByteArray.asSuccess()
|
} returns encryptedByteArray.asSuccess()
|
||||||
|
coEvery {
|
||||||
|
vaultDiskSource.saveSend(
|
||||||
|
userId,
|
||||||
|
sendFileResponse.createFileJsonResponse.sendResponse,
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
coEvery {
|
coEvery {
|
||||||
sendsService.createFileSend(body = createMockSendJsonRequest(number = 1))
|
sendsService.createFileSend(body = createMockSendJsonRequest(number = 1))
|
||||||
} returns sendFileResponse.asSuccess()
|
} returns sendFileResponse.asSuccess()
|
||||||
coEvery {
|
coEvery {
|
||||||
sendsService.uploadFile(
|
sendsService.uploadFile(
|
||||||
sendFileResponse = sendFileResponse,
|
sendFileResponse = sendFileResponse.createFileJsonResponse,
|
||||||
encryptedFile = encryptedByteArray,
|
encryptedFile = encryptedByteArray,
|
||||||
)
|
)
|
||||||
} returns Throwable("Fail").asFailure()
|
} returns Throwable().asFailure()
|
||||||
|
|
||||||
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = uri)
|
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = uri)
|
||||||
|
|
||||||
assertEquals(CreateSendResult.Error, result)
|
assertEquals(CreateSendResult.Error(null), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2588,11 +2600,11 @@ class VaultRepositoryTest {
|
||||||
coEvery {
|
coEvery {
|
||||||
vaultSdkSource.encryptSend(userId = userId, sendView = mockSendView)
|
vaultSdkSource.encryptSend(userId = userId, sendView = mockSendView)
|
||||||
} returns mockSdkSend.asSuccess()
|
} returns mockSdkSend.asSuccess()
|
||||||
coEvery { fileManager.uriToByteArray(any()) } returns Throwable("Fail").asFailure()
|
coEvery { fileManager.uriToByteArray(any()) } returns Throwable().asFailure()
|
||||||
|
|
||||||
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = uri)
|
val result = vaultRepository.createSend(sendView = mockSendView, fileUri = uri)
|
||||||
|
|
||||||
assertEquals(CreateSendResult.Error, result)
|
assertEquals(CreateSendResult.Error(message = null), result)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -2607,11 +2619,8 @@ class VaultRepositoryTest {
|
||||||
val mockSdkSend = createMockSdkSend(number = 1)
|
val mockSdkSend = createMockSdkSend(number = 1)
|
||||||
val byteArray = byteArrayOf(1)
|
val byteArray = byteArrayOf(1)
|
||||||
val encryptedByteArray = byteArrayOf(2)
|
val encryptedByteArray = byteArrayOf(2)
|
||||||
val sendResponse = createMockSend(number = 1)
|
val sendFileResponse = CreateFileSendResponse.Success(
|
||||||
val sendFileResponse = SendFileResponseJson(
|
createMockFileSendResponseJson(number = 1),
|
||||||
url = url,
|
|
||||||
fileUploadType = FileUploadType.AZURE,
|
|
||||||
sendResponse = sendResponse,
|
|
||||||
)
|
)
|
||||||
val mockSendViewResult = createMockSendView(number = 1)
|
val mockSendViewResult = createMockSendView(number = 1)
|
||||||
coEvery {
|
coEvery {
|
||||||
|
@ -2630,11 +2639,16 @@ class VaultRepositoryTest {
|
||||||
} returns sendFileResponse.asSuccess()
|
} returns sendFileResponse.asSuccess()
|
||||||
coEvery {
|
coEvery {
|
||||||
sendsService.uploadFile(
|
sendsService.uploadFile(
|
||||||
sendFileResponse = sendFileResponse,
|
sendFileResponse = sendFileResponse.createFileJsonResponse,
|
||||||
encryptedFile = encryptedByteArray,
|
encryptedFile = encryptedByteArray,
|
||||||
)
|
)
|
||||||
} returns sendResponse.asSuccess()
|
} returns sendFileResponse.createFileJsonResponse.sendResponse.asSuccess()
|
||||||
coEvery { vaultDiskSource.saveSend(userId, sendResponse) } just runs
|
coEvery {
|
||||||
|
vaultDiskSource.saveSend(
|
||||||
|
userId,
|
||||||
|
sendFileResponse.createFileJsonResponse.sendResponse,
|
||||||
|
)
|
||||||
|
} just runs
|
||||||
coEvery {
|
coEvery {
|
||||||
vaultSdkSource.decryptSend(userId, mockSdkSend)
|
vaultSdkSource.decryptSend(userId, mockSdkSend)
|
||||||
} returns mockSendViewResult.asSuccess()
|
} returns mockSendViewResult.asSuccess()
|
||||||
|
|
|
@ -259,7 +259,7 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
||||||
every { viewState.toSendView(clock) } returns mockSendView
|
every { viewState.toSendView(clock) } returns mockSendView
|
||||||
coEvery {
|
coEvery {
|
||||||
vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
vaultRepository.createSend(sendView = mockSendView, fileUri = null)
|
||||||
} returns CreateSendResult.Error
|
} returns CreateSendResult.Error("Fail")
|
||||||
val viewModel = createViewModel(initialState)
|
val viewModel = createViewModel(initialState)
|
||||||
|
|
||||||
viewModel.stateFlow.test {
|
viewModel.stateFlow.test {
|
||||||
|
@ -277,7 +277,7 @@ class AddSendViewModelTest : BaseViewModelTest() {
|
||||||
initialState.copy(
|
initialState.copy(
|
||||||
dialogState = AddSendState.DialogState.Error(
|
dialogState = AddSendState.DialogState.Error(
|
||||||
title = R.string.an_error_has_occurred.asText(),
|
title = R.string.an_error_has_occurred.asText(),
|
||||||
message = R.string.generic_error_message.asText(),
|
message = "Fail".asText(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
awaitItem(),
|
awaitItem(),
|
||||||
|
|
Loading…
Reference in a new issue