mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
Adding the Folder Api and Service (#809)
This commit is contained in:
parent
ab5a35b914
commit
e38dea7ab7
7 changed files with 263 additions and 0 deletions
|
@ -0,0 +1,36 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.api
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
|
||||
/**
|
||||
* Defines raw calls under the /folders API with authentication applied.
|
||||
*/
|
||||
interface FoldersApi {
|
||||
|
||||
/**
|
||||
* Create a folder.
|
||||
*/
|
||||
@POST("folders")
|
||||
suspend fun createFolder(@Body body: FolderJsonRequest): Result<SyncResponseJson.Folder>
|
||||
|
||||
/**
|
||||
* Updates a folder.
|
||||
*/
|
||||
@PUT("folders/{folderId}")
|
||||
suspend fun updateFolder(
|
||||
@Path("folderId") folderId: String,
|
||||
@Body body: FolderJsonRequest,
|
||||
): Result<SyncResponseJson.Folder>
|
||||
|
||||
/**
|
||||
* Deletes a folder.
|
||||
*/
|
||||
@DELETE("folders/{folderId}")
|
||||
suspend fun deleteFolder(@Path("folderId") folderId: String): Result<Unit>
|
||||
}
|
|
@ -3,6 +3,8 @@ package com.x8bit.bitwarden.data.vault.datasource.network.di
|
|||
import com.x8bit.bitwarden.data.platform.datasource.network.retrofit.Retrofits
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.CiphersService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.CiphersServiceImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.FolderService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.FolderServiceImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SendsService
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SendsServiceImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
|
||||
|
@ -41,6 +43,16 @@ object VaultNetworkModule {
|
|||
clock = clock,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesFolderService(
|
||||
retrofits: Retrofits,
|
||||
json: Json,
|
||||
): FolderService = FolderServiceImpl(
|
||||
foldersApi = retrofits.authenticatedApiRetrofit.create(),
|
||||
json = json,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSendsService(
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Represents a folder request.
|
||||
*
|
||||
* @property name The name of the folder.
|
||||
*/
|
||||
@Serializable
|
||||
data class FolderJsonRequest(
|
||||
@SerialName("name")
|
||||
val name: String?,
|
||||
)
|
|
@ -0,0 +1,33 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.model
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/**
|
||||
* Models the responses from the folder update request.
|
||||
*/
|
||||
sealed class UpdateFolderResponseJson {
|
||||
/**
|
||||
* The request completed successfully and returned the updated [folder].
|
||||
*/
|
||||
data class Success(
|
||||
val folder: SyncResponseJson.Folder,
|
||||
) : UpdateFolderResponseJson()
|
||||
|
||||
/**
|
||||
* 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>>?,
|
||||
) : UpdateFolderResponseJson()
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.service
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateFolderResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
|
||||
/**
|
||||
* Provides an API for querying folder endpoints.
|
||||
*/
|
||||
interface FolderService {
|
||||
/**
|
||||
* Attempt to create a folder.
|
||||
*/
|
||||
suspend fun createFolder(body: FolderJsonRequest): Result<SyncResponseJson.Folder>
|
||||
|
||||
/**
|
||||
* Attempt to update a folder.
|
||||
*/
|
||||
suspend fun updateFolder(
|
||||
folderId: String,
|
||||
body: FolderJsonRequest,
|
||||
): Result<UpdateFolderResponseJson>
|
||||
|
||||
/**
|
||||
* Attempt to hard delete a folder.
|
||||
*/
|
||||
suspend fun deleteFolder(folderId: String): Result<Unit>
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
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.FoldersApi
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateFolderResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class FolderServiceImpl constructor(
|
||||
private val foldersApi: FoldersApi,
|
||||
private val json: Json,
|
||||
) : FolderService {
|
||||
override suspend fun createFolder(body: FolderJsonRequest): Result<SyncResponseJson.Folder> =
|
||||
foldersApi.createFolder(body = body)
|
||||
|
||||
override suspend fun updateFolder(
|
||||
folderId: String,
|
||||
body: FolderJsonRequest,
|
||||
): Result<UpdateFolderResponseJson> =
|
||||
foldersApi
|
||||
.updateFolder(
|
||||
folderId = folderId,
|
||||
body = body,
|
||||
)
|
||||
.map { UpdateFolderResponseJson.Success(folder = it) }
|
||||
.recoverCatching { throwable ->
|
||||
throwable
|
||||
.toBitwardenError()
|
||||
.parseErrorBodyOrNull<UpdateFolderResponseJson.Invalid>(
|
||||
code = 400,
|
||||
json = json,
|
||||
)
|
||||
?: throw throwable
|
||||
}
|
||||
|
||||
override suspend fun deleteFolder(folderId: String): Result<Unit> =
|
||||
foldersApi.deleteFolder(folderId = folderId)
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package com.x8bit.bitwarden.data.vault.datasource.network.service
|
||||
|
||||
import com.x8bit.bitwarden.data.platform.base.BaseServiceTest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.api.FoldersApi
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.FolderJsonRequest
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.UpdateFolderResponseJson
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okhttp3.mockwebserver.MockResponse
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Test
|
||||
import retrofit2.create
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
class FoldersServiceTest : BaseServiceTest() {
|
||||
private val folderApi: FoldersApi = retrofit.create()
|
||||
|
||||
private val folderService: FolderService = FolderServiceImpl(
|
||||
foldersApi = folderApi,
|
||||
json = json,
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `createFolder should return the correct response`() = runTest {
|
||||
server.enqueue(MockResponse().setBody(CREATE_UPDATE_FOLDER_SUCCESS_JSON))
|
||||
val result = folderService.createFolder(
|
||||
body = FolderJsonRequest(DEFAULT_NAME),
|
||||
)
|
||||
assertEquals(
|
||||
DEFAULT_FOLDER,
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateFolder with success response should return a Success with the correct folder`() =
|
||||
runTest {
|
||||
server.enqueue(MockResponse().setBody(CREATE_UPDATE_FOLDER_SUCCESS_JSON))
|
||||
val result = folderService.updateFolder(
|
||||
folderId = DEFAULT_ID,
|
||||
body = FolderJsonRequest(DEFAULT_NAME),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
UpdateFolderResponseJson.Success(DEFAULT_FOLDER),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateFolder with invalid response should return an Invalid with the correct data`() =
|
||||
runTest {
|
||||
server.enqueue(MockResponse().setResponseCode(400).setBody(UPDATE_FOLDER_INVALID_JSON))
|
||||
val result = folderService.updateFolder(
|
||||
folderId = DEFAULT_ID,
|
||||
body = FolderJsonRequest(DEFAULT_NAME),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
UpdateFolderResponseJson.Invalid(
|
||||
message = "You do not have permission to edit this.",
|
||||
validationErrors = null,
|
||||
),
|
||||
result.getOrThrow(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `DeleteFolder should return a Success with the correct data`() = runTest {
|
||||
server.enqueue(MockResponse().setResponseCode(200))
|
||||
val result = folderService.deleteFolder(DEFAULT_ID)
|
||||
assertEquals(Unit, result.getOrThrow())
|
||||
}
|
||||
}
|
||||
|
||||
private const val DEFAULT_ID = "FolderId"
|
||||
private const val DEFAULT_NAME = "TestName"
|
||||
|
||||
private val DEFAULT_FOLDER = SyncResponseJson.Folder(
|
||||
id = DEFAULT_ID,
|
||||
name = DEFAULT_NAME,
|
||||
revisionDate = ZonedDateTime.parse("2024-01-24T22:40:17.1559611Z"),
|
||||
)
|
||||
|
||||
private const val CREATE_UPDATE_FOLDER_SUCCESS_JSON = """
|
||||
{
|
||||
"id":"FolderId",
|
||||
"name":"TestName",
|
||||
"revisionDate":"2024-01-24T22:40:17.1559611Z",
|
||||
"object":"folder"
|
||||
}
|
||||
"""
|
||||
|
||||
private const val UPDATE_FOLDER_INVALID_JSON = """
|
||||
{
|
||||
"message": "You do not have permission to edit this.",
|
||||
"validationErrors": null
|
||||
}
|
||||
"""
|
Loading…
Reference in a new issue