mirror of
https://github.com/bitwarden/android.git
synced 2026-07-29 17:33:28 -05:00
Add sends database table (#490)
This commit is contained in:
committed by
Álison Fernandes
parent
cd707473fc
commit
b6032873ec
@@ -23,6 +23,11 @@ interface VaultDiskSource {
|
||||
*/
|
||||
fun getFolders(userId: String): Flow<List<SyncResponseJson.Folder>>
|
||||
|
||||
/**
|
||||
* Retrieves all sends from the data source for a given [userId].
|
||||
*/
|
||||
fun getSends(userId: String): Flow<List<SyncResponseJson.Send>>
|
||||
|
||||
/**
|
||||
* Replaces all [vault] data for a given [userId] with the new `vault`.
|
||||
*
|
||||
|
||||
+37
@@ -4,9 +4,11 @@ import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CiphersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CollectionsDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FoldersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.SendsDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CipherEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CollectionEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.FolderEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.SendEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
@@ -24,6 +26,7 @@ class VaultDiskSourceImpl(
|
||||
private val ciphersDao: CiphersDao,
|
||||
private val collectionsDao: CollectionsDao,
|
||||
private val foldersDao: FoldersDao,
|
||||
private val sendsDao: SendsDao,
|
||||
private val json: Json,
|
||||
) : VaultDiskSource {
|
||||
|
||||
@@ -31,6 +34,7 @@ class VaultDiskSourceImpl(
|
||||
private val forceCollectionsFlow =
|
||||
bufferedMutableSharedFlow<List<SyncResponseJson.Collection>>()
|
||||
private val forceFolderFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Folder>>()
|
||||
private val forceSendFlow = bufferedMutableSharedFlow<List<SyncResponseJson.Send>>()
|
||||
|
||||
override fun getCiphers(
|
||||
userId: String,
|
||||
@@ -85,6 +89,21 @@ class VaultDiskSourceImpl(
|
||||
},
|
||||
)
|
||||
|
||||
override fun getSends(
|
||||
userId: String,
|
||||
): Flow<List<SyncResponseJson.Send>> =
|
||||
merge(
|
||||
forceSendFlow,
|
||||
sendsDao
|
||||
.getAllSends(userId = userId)
|
||||
.map { entities ->
|
||||
entities.map { entity ->
|
||||
json.decodeFromString<SyncResponseJson.Send>(entity.sendJson)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override suspend fun replaceVaultData(
|
||||
userId: String,
|
||||
vault: SyncResponseJson,
|
||||
@@ -132,6 +151,19 @@ class VaultDiskSourceImpl(
|
||||
},
|
||||
)
|
||||
}
|
||||
val deferredSends = async {
|
||||
sendsDao.replaceAllSends(
|
||||
userId = userId,
|
||||
sends = vault.sends.orEmpty().map { send ->
|
||||
SendEntity(
|
||||
userId = userId,
|
||||
id = send.id,
|
||||
sendType = json.encodeToString(send.type),
|
||||
sendJson = json.encodeToString(send),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
// When going from 0 items to 0 items, the respective dao flow will not re-emit
|
||||
// So we use this to give it a little push.
|
||||
if (!deferredCiphers.await()) {
|
||||
@@ -143,6 +175,9 @@ class VaultDiskSourceImpl(
|
||||
if (!deferredFolders.await()) {
|
||||
forceFolderFlow.tryEmit(emptyList())
|
||||
}
|
||||
if (!deferredSends.await()) {
|
||||
forceSendFlow.tryEmit(emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,10 +186,12 @@ class VaultDiskSourceImpl(
|
||||
val deferredCiphers = async { ciphersDao.deleteAllCiphers(userId = userId) }
|
||||
val deferredCollections = async { collectionsDao.deleteAllCollections(userId = userId) }
|
||||
val deferredFolders = async { foldersDao.deleteAllFolders(userId = userId) }
|
||||
val deferredSends = async { sendsDao.deleteAllSends(userId = userId) }
|
||||
awaitAll(
|
||||
deferredCiphers,
|
||||
deferredCollections,
|
||||
deferredFolders,
|
||||
deferredSends,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.x8bit.bitwarden.data.vault.datasource.disk.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.SendEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Provides methods for inserting, retrieving, and deleting sends from the database using the
|
||||
* [SendEntity].
|
||||
*/
|
||||
@Dao
|
||||
interface SendsDao {
|
||||
|
||||
/**
|
||||
* Inserts multiple sends into the database.
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertSends(sends: List<SendEntity>)
|
||||
|
||||
/**
|
||||
* Retrieves all sends from the database for a given [userId].
|
||||
*/
|
||||
@Query("SELECT * FROM sends WHERE user_id = :userId")
|
||||
fun getAllSends(
|
||||
userId: String,
|
||||
): Flow<List<SendEntity>>
|
||||
|
||||
/**
|
||||
* Deletes all the stored sends associated with the given [userId]. This will return the
|
||||
* number of rows deleted by this query.
|
||||
*/
|
||||
@Query("DELETE FROM sends WHERE user_id = :userId")
|
||||
suspend fun deleteAllSends(userId: String): Int
|
||||
|
||||
/**
|
||||
* Deletes all the stored sends associated with the given [userId] and then add all new
|
||||
* [sends] to the database. This will return `true` if any changes were made to the database
|
||||
* and `false` otherwise.
|
||||
*/
|
||||
@Transaction
|
||||
suspend fun replaceAllSends(userId: String, sends: List<SendEntity>): Boolean {
|
||||
val deletedSendsCount = deleteAllSends(userId)
|
||||
insertSends(sends)
|
||||
return deletedSendsCount > 0 || sends.isNotEmpty()
|
||||
}
|
||||
}
|
||||
+9
-1
@@ -7,9 +7,11 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.convertor.ZonedDateTimeTyp
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CiphersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CollectionsDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FoldersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.SendsDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CipherEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CollectionEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.FolderEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.SendEntity
|
||||
|
||||
/**
|
||||
* Room database for storing any persisted data from the vault sync.
|
||||
@@ -19,8 +21,9 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.entity.FolderEntity
|
||||
CipherEntity::class,
|
||||
CollectionEntity::class,
|
||||
FolderEntity::class,
|
||||
SendEntity::class,
|
||||
],
|
||||
version = 1,
|
||||
version = 2,
|
||||
)
|
||||
@TypeConverters(ZonedDateTimeTypeConverter::class)
|
||||
abstract class VaultDatabase : RoomDatabase() {
|
||||
@@ -39,4 +42,9 @@ abstract class VaultDatabase : RoomDatabase() {
|
||||
* Provides the DAO for accessing folder data.
|
||||
*/
|
||||
abstract fun folderDao(): FoldersDao
|
||||
|
||||
/**
|
||||
* Provides the DAO for accessing send data.
|
||||
*/
|
||||
abstract fun sendsDao(): SendsDao
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.convertor.ZonedDateTimeTyp
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CiphersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CollectionsDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FoldersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.SendsDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.database.VaultDatabase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
@@ -32,6 +33,7 @@ class VaultDiskModule {
|
||||
klass = VaultDatabase::class.java,
|
||||
name = "vault_database",
|
||||
)
|
||||
.fallbackToDestructiveMigration()
|
||||
.addTypeConverter(ZonedDateTimeTypeConverter())
|
||||
.build()
|
||||
|
||||
@@ -47,17 +49,23 @@ class VaultDiskModule {
|
||||
@Singleton
|
||||
fun provideFolderDao(database: VaultDatabase): FoldersDao = database.folderDao()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSendDao(database: VaultDatabase): SendsDao = database.sendsDao()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideVaultDiskSource(
|
||||
ciphersDao: CiphersDao,
|
||||
collectionsDao: CollectionsDao,
|
||||
foldersDao: FoldersDao,
|
||||
sendsDao: SendsDao,
|
||||
json: Json,
|
||||
): VaultDiskSource = VaultDiskSourceImpl(
|
||||
ciphersDao = ciphersDao,
|
||||
collectionsDao = collectionsDao,
|
||||
foldersDao = foldersDao,
|
||||
sendsDao = sendsDao,
|
||||
json = json,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.x8bit.bitwarden.data.vault.datasource.disk.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
/**
|
||||
* Entity representing a send in the database.
|
||||
*/
|
||||
@Entity(tableName = "sends")
|
||||
data class SendEntity(
|
||||
@PrimaryKey(autoGenerate = false)
|
||||
@ColumnInfo(name = "id")
|
||||
val id: String,
|
||||
|
||||
@ColumnInfo(name = "user_id", index = true)
|
||||
val userId: String,
|
||||
|
||||
@ColumnInfo(name = "send_type")
|
||||
val sendType: String,
|
||||
|
||||
@ColumnInfo(name = "send_json")
|
||||
val sendJson: String,
|
||||
)
|
||||
+22
-15
@@ -164,6 +164,12 @@ class VaultRepositoryImpl(
|
||||
observeVaultDiskCollections(activeUserId)
|
||||
}
|
||||
.launchIn(unconfinedScope)
|
||||
// Setup sends MutableStateFlow
|
||||
mutableSendDataStateFlow
|
||||
.observeWhenSubscribedAndLoggedIn(authDiskSource.userStateFlow) { activeUserId ->
|
||||
observeVaultDiskSends(activeUserId)
|
||||
}
|
||||
.launchIn(unconfinedScope)
|
||||
}
|
||||
|
||||
override fun clearUnlockedData() {
|
||||
@@ -201,7 +207,6 @@ class VaultRepositoryImpl(
|
||||
unlockVaultForOrganizationsIfNecessary(syncResponse = syncResponse)
|
||||
storeProfileData(syncResponse = syncResponse)
|
||||
vaultDiskSource.replaceVaultData(userId = userId, vault = syncResponse)
|
||||
decryptSendsAndUpdateSendDataState(sendList = syncResponse.sends)
|
||||
},
|
||||
onFailure = { throwable ->
|
||||
mutableCiphersStateFlow.update { currentState ->
|
||||
@@ -474,20 +479,6 @@ class VaultRepositoryImpl(
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun decryptSendsAndUpdateSendDataState(sendList: List<SyncResponseJson.Send>?) {
|
||||
val newState = vaultSdkSource
|
||||
.decryptSendList(
|
||||
sendList = sendList
|
||||
.orEmpty()
|
||||
.toEncryptedSdkSendList(),
|
||||
)
|
||||
.fold(
|
||||
onSuccess = { DataState.Loaded(data = SendData(sendViewList = it)) },
|
||||
onFailure = { DataState.Error(error = it) },
|
||||
)
|
||||
mutableSendDataStateFlow.update { newState }
|
||||
}
|
||||
|
||||
private fun observeVaultDiskCiphers(
|
||||
userId: String,
|
||||
): Flow<DataState<List<CipherView>>> =
|
||||
@@ -535,6 +526,22 @@ class VaultRepositoryImpl(
|
||||
)
|
||||
}
|
||||
.onEach { mutableCollectionsStateFlow.value = it }
|
||||
|
||||
private fun observeVaultDiskSends(
|
||||
userId: String,
|
||||
): Flow<DataState<SendData>> =
|
||||
vaultDiskSource
|
||||
.getSends(userId = userId)
|
||||
.onStart { mutableSendDataStateFlow.value = DataState.Loading }
|
||||
.map {
|
||||
vaultSdkSource
|
||||
.decryptSendList(sendList = it.toEncryptedSdkSendList())
|
||||
.fold(
|
||||
onSuccess = { sends -> DataState.Loaded(SendData(sends)) },
|
||||
onFailure = { throwable -> DataState.Error(throwable) },
|
||||
)
|
||||
}
|
||||
.onEach { mutableSendDataStateFlow.value = it }
|
||||
}
|
||||
|
||||
private fun <T> Throwable.toNetworkOrErrorState(data: T?): DataState<T> =
|
||||
|
||||
Reference in New Issue
Block a user