BIT-874: Expose Collections data from VaultRepository (#382)

This commit is contained in:
Brian Yencho 2023-12-13 10:00:15 -06:00 committed by Álison Fernandes
parent 12a816e678
commit 3d6111cd8b
10 changed files with 241 additions and 3 deletions

View file

@ -951,7 +951,7 @@ data class SyncResponseJson(
*
* @property organizationId The organization ID of the collection.
* @property shouldHidePasswords If the collection should hide passwords.
* @property name The name of the collection (nullable).
* @property name The name of the collection.
* @property externalId The external ID of the collection (nullable).
* @property isReadOnly If the collection is marked as read only.
* @property id The ID of the collection.
@ -965,7 +965,7 @@ data class SyncResponseJson(
val shouldHidePasswords: Boolean,
@SerialName("name")
val name: String?,
val name: String,
@SerialName("externalId")
val externalId: String?,

View file

@ -29,6 +29,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.VaultState
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedNetworkCipher
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipherList
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCollectionList
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolderList
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkSendList
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult
@ -399,9 +400,19 @@ class VaultRepositoryImpl constructor(
.toEncryptedSdkFolderList(),
)
},
) { decryptedCipherList, decryptedFolderList ->
{
vaultSdkSource
.decryptCollectionList(
collectionList = syncResponse
.collections
.orEmpty()
.toEncryptedSdkCollectionList(),
)
},
) { decryptedCipherList, decryptedFolderList, decryptedCollectionList ->
VaultData(
cipherViewList = decryptedCipherList,
collectionViewList = decryptedCollectionList,
folderViewList = decryptedFolderList,
)
}

View file

@ -1,15 +1,18 @@
package com.x8bit.bitwarden.data.vault.repository.model
import com.bitwarden.core.CipherView
import com.bitwarden.core.CollectionView
import com.bitwarden.core.FolderView
/**
* Represents decrypted vault data.
*
* @param cipherViewList List of decrypted ciphers.
* @param collectionViewList List of decrypted collections.
* @param folderViewList List of decrypted folders.
*/
data class VaultData(
val cipherViewList: List<CipherView>,
val collectionViewList: List<CollectionView>,
val folderViewList: List<FolderView>,
)

View file

@ -0,0 +1,25 @@
package com.x8bit.bitwarden.data.vault.repository.util
import com.bitwarden.core.Collection
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
/**
* Converts a [SyncResponseJson.Collection] object to a corresponding Bitwarden SDK [Collection]
* object.
*/
fun SyncResponseJson.Collection.toEncryptedSdkCollection(): Collection =
Collection(
id = this.id,
organizationId = this.organizationId,
name = this.name,
externalId = this.externalId,
hidePasswords = this.shouldHidePasswords,
readOnly = this.isReadOnly,
)
/**
* Converts a list of [SyncResponseJson.Collection] objects to a list of corresponding
* Bitwarden SDK [Collection] objects.
*/
fun List<SyncResponseJson.Collection>.toEncryptedSdkCollectionList(): List<Collection> =
map { it.toEncryptedSdkCollection() }

View file

@ -0,0 +1,16 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
import com.bitwarden.core.CollectionView
/**
* Create a mock [CollectionView] with a given [number].
*/
fun createMockCollectionView(number: Int): CollectionView =
CollectionView(
id = "mockId-$number",
organizationId = "mockOrganizationId-$number",
hidePasswords = false,
name = "mockName-$number",
externalId = "mockExternalId-$number",
readOnly = false,
)

View file

@ -0,0 +1,16 @@
package com.x8bit.bitwarden.data.vault.datasource.sdk.model
import com.bitwarden.core.Collection
/**
* Create a mock [Collection] with a given [number].
*/
fun createMockSdkCollection(number: Int): Collection =
Collection(
id = "mockId-$number",
organizationId = "mockOrganizationId-$number",
hidePasswords = false,
name = "mockName-$number",
externalId = "mockExternalId-$number",
readOnly = false,
)

View file

@ -26,8 +26,10 @@ import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCipher
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkCollection
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkFolder
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSdkSend
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockSendView
@ -87,6 +89,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -124,6 +129,7 @@ class VaultRepositoryTest {
DataState.Loaded(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -165,6 +171,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -180,6 +189,7 @@ class VaultRepositoryTest {
DataState.Loaded(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -190,6 +200,7 @@ class VaultRepositoryTest {
DataState.Pending(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -199,6 +210,7 @@ class VaultRepositoryTest {
DataState.Loaded(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -226,6 +238,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -285,6 +300,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -318,6 +336,45 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns mockException.asFailure()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
fakeAuthDiskSource.userState = MOCK_USER_STATE
vaultRepository.sync()
assertEquals(
DataState.Error<VaultData>(error = mockException),
vaultRepository.vaultDataStateFlow.value,
)
}
@Test
fun `sync with decryptCollectionList Failure should update vaultDataStateFlow with Error`() =
runTest {
val mockException = IllegalStateException()
coEvery {
syncService.sync()
} returns Result.success(createMockSyncResponse(number = 1))
coEvery {
vaultSdkSource.initializeOrganizationCrypto(
request = InitOrgCryptoRequest(
organizationKeys = createMockOrganizationKeys(1),
),
)
} returns InitializeCryptoResult.Success.asSuccess()
coEvery {
vaultSdkSource.decryptCipherList(listOf(createMockSdkCipher(1)))
} returns listOf(createMockCipherView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns mockException.asFailure()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -351,6 +408,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns mockException.asFailure()
@ -434,6 +494,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -449,6 +512,7 @@ class VaultRepositoryTest {
DataState.Loaded(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -462,6 +526,7 @@ class VaultRepositoryTest {
DataState.Pending(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -471,6 +536,7 @@ class VaultRepositoryTest {
DataState.NoNetwork(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -501,6 +567,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -608,6 +677,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -680,6 +752,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -1518,6 +1593,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -1533,6 +1611,7 @@ class VaultRepositoryTest {
DataState.Loaded(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -1567,6 +1646,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(number = 1)))
} returns listOf(createMockSendView(number = 1)).asSuccess()
@ -1617,6 +1699,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(itemId)))
} returns listOf(createMockFolderView(1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(itemId)))
} returns listOf(createMockCollectionView(itemId)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(itemId)))
} returns listOf(createMockSendView(itemId)).asSuccess()
@ -1698,6 +1783,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(1)))
} returns listOf(createMockSendView(1)).asSuccess()
@ -1737,6 +1825,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(folderId)))
} returns listOf(createMockFolderView(folderId)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(folderId)))
} returns listOf(createMockCollectionView(folderId)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(folderId)))
} returns listOf(createMockSendView(folderId)).asSuccess()
@ -1818,6 +1909,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(1)))
} returns listOf(createMockSendView(1)).asSuccess()
@ -1903,6 +1997,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(1)))
} returns listOf(createMockSendView(1)).asSuccess()
@ -1987,6 +2084,9 @@ class VaultRepositoryTest {
coEvery {
vaultSdkSource.decryptFolderList(listOf(createMockSdkFolder(1)))
} returns listOf(createMockFolderView(1)).asSuccess()
coEvery {
vaultSdkSource.decryptCollectionList(listOf(createMockSdkCollection(1)))
} returns listOf(createMockCollectionView(number = 1)).asSuccess()
coEvery {
vaultSdkSource.decryptSendList(listOf(createMockSdkSend(1)))
} returns listOf(createMockSendView(1)).asSuccess()

View file

@ -0,0 +1,59 @@
package com.x8bit.bitwarden.data.vault.repository.util
import com.bitwarden.core.Collection
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import org.junit.Test
import org.junit.jupiter.api.Assertions.assertEquals
class VaultSdkCollectionExtensionsTest {
@Test
fun `toEncryptedSdkCollection should convert a network Collection to an SDK Collection`() {
assertEquals(
Collection(
organizationId = "organizationId",
hidePasswords = true,
name = "name",
externalId = "externalId",
readOnly = true,
id = "id",
),
SyncResponseJson.Collection(
organizationId = "organizationId",
shouldHidePasswords = true,
name = "name",
externalId = "externalId",
isReadOnly = true,
id = "id",
)
.toEncryptedSdkCollection(),
)
}
@Suppress("MaxLineLength")
@Test
fun `toEncryptedSdkCollectionList should convert a list of network Collections to a list of SDK Collections`() {
assertEquals(
listOf(
Collection(
organizationId = "organizationId",
hidePasswords = true,
name = "name",
externalId = "externalId",
readOnly = true,
id = "id",
),
),
listOf(
SyncResponseJson.Collection(
organizationId = "organizationId",
shouldHidePasswords = true,
name = "name",
externalId = "externalId",
isReadOnly = true,
id = "id",
)
.toEncryptedSdkCollection(),
),
)
}
}

View file

@ -8,6 +8,7 @@ import com.x8bit.bitwarden.data.auth.repository.model.UserState.SpecialCircumsta
import com.x8bit.bitwarden.data.platform.repository.model.DataState
import com.x8bit.bitwarden.data.platform.repository.model.Environment
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
@ -260,6 +261,7 @@ class VaultViewModelTest : BaseViewModelTest() {
value = DataState.Loaded(
data = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
),
),
@ -296,6 +298,7 @@ class VaultViewModelTest : BaseViewModelTest() {
value = DataState.Loaded(
data = VaultData(
cipherViewList = emptyList(),
collectionViewList = emptyList(),
folderViewList = emptyList(),
),
),
@ -384,6 +387,7 @@ class VaultViewModelTest : BaseViewModelTest() {
mutableVaultDataStateFlow.value = DataState.Loaded(
data = VaultData(
cipherViewList = emptyList(),
collectionViewList = emptyList(),
folderViewList = emptyList(),
),
)

View file

@ -10,6 +10,7 @@ import com.bitwarden.core.SecureNoteType
import com.bitwarden.core.SecureNoteView
import com.bitwarden.core.UriMatchType
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCipherView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockCollectionView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.createMockFolderView
import com.x8bit.bitwarden.data.vault.repository.model.VaultData
import com.x8bit.bitwarden.ui.platform.base.util.asText
@ -35,6 +36,7 @@ class VaultDataExtensionsTest {
fun `toViewState should transform full VaultData into ViewState Content`() {
val vaultData = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
)
@ -65,6 +67,7 @@ class VaultDataExtensionsTest {
fun `toViewState should transform empty VaultData into ViewState NoItems`() {
val vaultData = VaultData(
cipherViewList = emptyList(),
collectionViewList = emptyList(),
folderViewList = emptyList(),
)
@ -80,6 +83,7 @@ class VaultDataExtensionsTest {
fun `toViewState should not transform ciphers with no ID into ViewState items`() {
val vaultData = VaultData(
cipherViewList = listOf(createMockCipherView(number = 1).copy(id = null)),
collectionViewList = listOf(createMockCollectionView(number = 1)),
folderViewList = listOf(createMockFolderView(number = 1)),
)