mirror of
https://github.com/bitwarden/android.git
synced 2026-06-07 23:58:03 -05:00
BIT-431: Add a table to the vault database for folders (#403)
This commit is contained in:
committed by
Álison Fernandes
parent
34101245dd
commit
34e3fbcc04
@@ -13,6 +13,11 @@ interface VaultDiskSource {
|
||||
*/
|
||||
fun getCiphers(userId: String): Flow<List<SyncResponseJson.Cipher>>
|
||||
|
||||
/**
|
||||
* Retrieves all folders from the data source for a given [userId].
|
||||
*/
|
||||
fun getFolders(userId: String): Flow<List<SyncResponseJson.Folder>>
|
||||
|
||||
/**
|
||||
* Replaces all [vault] data for a given [userId] with the new `vault`.
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
package com.x8bit.bitwarden.data.vault.datasource.disk
|
||||
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CiphersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FoldersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CipherEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.FolderEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.serialization.encodeToString
|
||||
@@ -13,6 +18,7 @@ import kotlinx.serialization.json.Json
|
||||
*/
|
||||
class VaultDiskSourceImpl(
|
||||
private val ciphersDao: CiphersDao,
|
||||
private val foldersDao: FoldersDao,
|
||||
private val json: Json,
|
||||
) : VaultDiskSource {
|
||||
|
||||
@@ -27,21 +33,67 @@ class VaultDiskSourceImpl(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun replaceVaultData(userId: String, vault: SyncResponseJson) {
|
||||
ciphersDao.replaceAllCiphers(
|
||||
userId = userId,
|
||||
ciphers = vault.ciphers.orEmpty().map { cipher ->
|
||||
CipherEntity(
|
||||
id = cipher.id,
|
||||
override fun getFolders(
|
||||
userId: String,
|
||||
): Flow<List<SyncResponseJson.Folder>> =
|
||||
foldersDao
|
||||
.getAllFolders(userId = userId)
|
||||
.map { entities ->
|
||||
entities.map { entity ->
|
||||
SyncResponseJson.Folder(
|
||||
id = entity.id,
|
||||
name = entity.name,
|
||||
revisionDate = entity.revisionDate,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun replaceVaultData(
|
||||
userId: String,
|
||||
vault: SyncResponseJson,
|
||||
) {
|
||||
coroutineScope {
|
||||
val deferredCiphers = async {
|
||||
ciphersDao.replaceAllCiphers(
|
||||
userId = userId,
|
||||
cipherType = json.encodeToString(cipher.type),
|
||||
cipherJson = json.encodeToString(cipher),
|
||||
ciphers = vault.ciphers.orEmpty().map { cipher ->
|
||||
CipherEntity(
|
||||
id = cipher.id,
|
||||
userId = userId,
|
||||
cipherType = json.encodeToString(cipher.type),
|
||||
cipherJson = json.encodeToString(cipher),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
val deferredFolders = async {
|
||||
foldersDao.replaceAllFolders(
|
||||
userId = userId,
|
||||
folders = vault.folders.orEmpty().map { folder ->
|
||||
FolderEntity(
|
||||
userId = userId,
|
||||
id = folder.id,
|
||||
name = folder.name,
|
||||
revisionDate = folder.revisionDate,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
awaitAll(
|
||||
deferredCiphers,
|
||||
deferredFolders,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteVaultData(userId: String) {
|
||||
ciphersDao.deleteAllCiphers(userId = userId)
|
||||
coroutineScope {
|
||||
val deferredCiphers = async { ciphersDao.deleteAllCiphers(userId = userId) }
|
||||
val deferredFolders = async { foldersDao.deleteAllFolders(userId = userId) }
|
||||
awaitAll(
|
||||
deferredCiphers,
|
||||
deferredFolders,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.x8bit.bitwarden.data.vault.datasource.disk.convertor
|
||||
|
||||
import androidx.room.ProvidedTypeConverter
|
||||
import androidx.room.TypeConverter
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/**
|
||||
* A [TypeConverter] to convert a [ZonedDateTime] to and from a [Long].
|
||||
*/
|
||||
@ProvidedTypeConverter
|
||||
object ZonedDateTimeTypeConverter {
|
||||
/**
|
||||
* A [TypeConverter] to convert a [Long] to a [ZonedDateTime].
|
||||
*/
|
||||
@TypeConverter
|
||||
fun fromTimestamp(
|
||||
value: Long?,
|
||||
): ZonedDateTime? = value?.let {
|
||||
ZonedDateTime.ofInstant(Instant.ofEpochSecond(it), ZoneOffset.UTC)
|
||||
}
|
||||
|
||||
/**
|
||||
* A [TypeConverter] to convert a [ZonedDateTime] to a [Long].
|
||||
*/
|
||||
@TypeConverter
|
||||
fun toTimestamp(
|
||||
localDateTime: ZonedDateTime?,
|
||||
): Long? = localDateTime?.toEpochSecond()
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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.FolderEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Provides methods for inserting, retrieving, and deleting folders from the database using the
|
||||
* [FolderEntity].
|
||||
*/
|
||||
@Dao
|
||||
interface FoldersDao {
|
||||
|
||||
/**
|
||||
* Inserts multiple folders into the database.
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertFolders(folders: List<FolderEntity>)
|
||||
|
||||
/**
|
||||
* Inserts a folder into the database.
|
||||
*/
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertFolder(folder: FolderEntity)
|
||||
|
||||
/**
|
||||
* Retrieves all folders from the database for a given [userId].
|
||||
*/
|
||||
@Query("SELECT * FROM folders WHERE user_id = :userId")
|
||||
fun getAllFolders(
|
||||
userId: String,
|
||||
): Flow<List<FolderEntity>>
|
||||
|
||||
/**
|
||||
* Deletes all the stored folders associated with the given [userId].
|
||||
*/
|
||||
@Query("DELETE FROM folders WHERE user_id = :userId")
|
||||
suspend fun deleteAllFolders(userId: String)
|
||||
|
||||
/**
|
||||
* Deletes the stored folder associated with the given [userId] that matches the [folderId].
|
||||
*/
|
||||
@Query("DELETE FROM folders WHERE user_id = :userId AND id = :folderId")
|
||||
suspend fun deleteFolder(userId: String, folderId: String)
|
||||
|
||||
/**
|
||||
* Deletes all the stored [folders] associated with the given [userId] and then add all new
|
||||
* `folders` to the database.
|
||||
*/
|
||||
@Transaction
|
||||
suspend fun replaceAllFolders(userId: String, folders: List<FolderEntity>) {
|
||||
deleteAllFolders(userId)
|
||||
insertFolders(folders)
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,12 @@ package com.x8bit.bitwarden.data.vault.datasource.disk.database
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.convertor.ZonedDateTimeTypeConverter
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CiphersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FoldersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CipherEntity
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.entity.FolderEntity
|
||||
|
||||
/**
|
||||
* Room database for storing any persisted data from the vault sync.
|
||||
@@ -11,13 +15,20 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.entity.CipherEntity
|
||||
@Database(
|
||||
entities = [
|
||||
CipherEntity::class,
|
||||
FolderEntity::class,
|
||||
],
|
||||
version = 1,
|
||||
)
|
||||
@TypeConverters(ZonedDateTimeTypeConverter::class)
|
||||
abstract class VaultDatabase : RoomDatabase() {
|
||||
|
||||
/**
|
||||
* Provides the DAO for accessing cipher data.
|
||||
*/
|
||||
abstract fun cipherDao(): CiphersDao
|
||||
|
||||
/**
|
||||
* Provides the DAO for accessing folder data.
|
||||
*/
|
||||
abstract fun folderDao(): FoldersDao
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import android.app.Application
|
||||
import androidx.room.Room
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSourceImpl
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.convertor.ZonedDateTimeTypeConverter
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.CiphersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.dao.FoldersDao
|
||||
import com.x8bit.bitwarden.data.vault.datasource.disk.database.VaultDatabase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
@@ -29,19 +31,26 @@ class VaultDiskModule {
|
||||
klass = VaultDatabase::class.java,
|
||||
name = "vault_database",
|
||||
)
|
||||
.addTypeConverter(ZonedDateTimeTypeConverter)
|
||||
.build()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCipherDao(database: VaultDatabase): CiphersDao = database.cipherDao()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideFolderDao(database: VaultDatabase): FoldersDao = database.folderDao()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideVaultDiskSource(
|
||||
ciphersDao: CiphersDao,
|
||||
foldersDao: FoldersDao,
|
||||
json: Json,
|
||||
): VaultDiskSource = VaultDiskSourceImpl(
|
||||
ciphersDao = ciphersDao,
|
||||
foldersDao = foldersDao,
|
||||
json = json,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.x8bit.bitwarden.data.vault.datasource.disk.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.time.ZonedDateTime
|
||||
|
||||
/**
|
||||
* Entity representing a folder in the database.
|
||||
*/
|
||||
@Entity(tableName = "folders")
|
||||
data class FolderEntity(
|
||||
@PrimaryKey(autoGenerate = false)
|
||||
@ColumnInfo(name = "id")
|
||||
val id: String,
|
||||
|
||||
@ColumnInfo(name = "user_id", index = true)
|
||||
val userId: String,
|
||||
|
||||
@ColumnInfo(name = "name")
|
||||
val name: String?,
|
||||
|
||||
@ColumnInfo(name = "revision_date")
|
||||
val revisionDate: ZonedDateTime,
|
||||
)
|
||||
Reference in New Issue
Block a user