refactor: Remove year old Room->Sqldelight db migration
This commit is contained in:
parent
1042d60af2
commit
16e02dee82
|
@ -3,17 +3,11 @@ package com.artemchep.keyguard.android.downloader.journal.room
|
|||
import android.content.Context
|
||||
import android.database.SQLException
|
||||
import androidx.room.Room
|
||||
import androidx.room.withTransaction
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.bind
|
||||
import com.artemchep.keyguard.common.io.effectMap
|
||||
import com.artemchep.keyguard.common.io.ioEffect
|
||||
import com.artemchep.keyguard.common.io.retry
|
||||
import com.artemchep.keyguard.common.io.shared
|
||||
import com.artemchep.keyguard.common.usecase.DeviceEncryptionKeyUseCase
|
||||
import com.artemchep.keyguard.data.Database
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
|
||||
|
@ -70,27 +64,4 @@ class DownloadDatabaseManager(
|
|||
block(db)
|
||||
}
|
||||
}
|
||||
|
||||
fun migrateIfExists(
|
||||
sqld: Database,
|
||||
): IO<Unit> = ioEffect(Dispatchers.IO) {
|
||||
val room = dbIo.bind()
|
||||
// Nothing to migrate.
|
||||
?: return@ioEffect
|
||||
room.withTransaction {
|
||||
val generatorHistory = room.generatorDao().getAll().first()
|
||||
sqld.transaction {
|
||||
generatorHistory.forEach { item ->
|
||||
sqld.generatorHistoryQueries.insert(
|
||||
value_ = item.value,
|
||||
createdAt = item.createdDate,
|
||||
isPassword = item.isPassword,
|
||||
isUsername = item.isUsername,
|
||||
isEmailRelay = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
room.generatorDao().removeAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,12 +8,11 @@ import app.cash.sqldelight.db.AfterVersion
|
|||
import app.cash.sqldelight.db.SqlDriver
|
||||
import app.cash.sqldelight.driver.android.AndroidSqliteDriver
|
||||
import com.artemchep.keyguard.android.downloader.ExportManagerImpl
|
||||
import com.artemchep.keyguard.android.downloader.journal.room.DownloadDatabaseManager
|
||||
import com.artemchep.keyguard.common.NotificationsWorker
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.bind
|
||||
import com.artemchep.keyguard.common.io.flatMap
|
||||
import com.artemchep.keyguard.common.io.ioEffect
|
||||
import com.artemchep.keyguard.common.io.ioUnit
|
||||
import com.artemchep.keyguard.common.model.MasterKey
|
||||
import com.artemchep.keyguard.common.service.export.ExportManager
|
||||
import com.artemchep.keyguard.common.usecase.QueueSyncAll
|
||||
|
@ -21,14 +20,11 @@ import com.artemchep.keyguard.common.usecase.QueueSyncById
|
|||
import com.artemchep.keyguard.copy.QueueSyncAllAndroid
|
||||
import com.artemchep.keyguard.copy.QueueSyncByIdAndroid
|
||||
import com.artemchep.keyguard.core.store.DatabaseManager
|
||||
import com.artemchep.keyguard.core.store.DatabaseManagerAndroid
|
||||
import com.artemchep.keyguard.core.store.DatabaseManagerImpl
|
||||
import com.artemchep.keyguard.core.store.SqlHelper
|
||||
import com.artemchep.keyguard.core.store.SqlManager
|
||||
import com.artemchep.keyguard.data.Database
|
||||
import com.artemchep.keyguard.feature.crashlytics.crashlyticsTap
|
||||
import com.artemchep.keyguard.provider.bitwarden.usecase.NotificationsImpl
|
||||
import com.artemchep.keyguard.room.RoomMigrationException
|
||||
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||
import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
|
||||
import org.kodein.di.DI
|
||||
|
@ -56,26 +52,10 @@ actual fun DI.Builder.createSubDi(
|
|||
NotificationsImpl(this)
|
||||
}
|
||||
bindSingleton<DatabaseManager> {
|
||||
val downloadDb = instance<DownloadDatabaseManager>()
|
||||
val roomDb = DatabaseManagerAndroid(
|
||||
applicationContext = instance(),
|
||||
json = instance(),
|
||||
name = "database",
|
||||
masterKey = masterKey,
|
||||
)
|
||||
val sqlManager: SqlManager = SqlManagerFile2(
|
||||
context = instance<Application>(),
|
||||
onCreate = { database ->
|
||||
roomDb
|
||||
.migrateIfExists(database)
|
||||
.flatMap {
|
||||
downloadDb.migrateIfExists(database)
|
||||
}
|
||||
// Log this exception, it's important to see how the migration
|
||||
// process goes. It's okay if it fails, but not great.
|
||||
.crashlyticsTap { e ->
|
||||
RoomMigrationException(e)
|
||||
}
|
||||
ioUnit()
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
@ -1,312 +0,0 @@
|
|||
package com.artemchep.keyguard.core.store
|
||||
|
||||
import android.content.Context
|
||||
import android.database.SQLException
|
||||
import android.util.Log
|
||||
import androidx.room.Room
|
||||
import com.artemchep.keyguard.common.io.IO
|
||||
import com.artemchep.keyguard.common.io.bind
|
||||
import com.artemchep.keyguard.common.io.effectMap
|
||||
import com.artemchep.keyguard.common.io.io
|
||||
import com.artemchep.keyguard.common.io.ioEffect
|
||||
import com.artemchep.keyguard.common.io.retry
|
||||
import com.artemchep.keyguard.common.io.shared
|
||||
import com.artemchep.keyguard.common.model.CipherHistoryType
|
||||
import com.artemchep.keyguard.common.model.MasterKey
|
||||
import com.artemchep.keyguard.data.Database
|
||||
import com.artemchep.keyguard.room.AppDatabase
|
||||
import com.artemchep.keyguard.room.RoomSecretConverter
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.zetetic.database.sqlcipher.SupportOpenHelperFactory
|
||||
|
||||
class DatabaseManagerAndroid(
|
||||
private val applicationContext: Context,
|
||||
private val json: Json,
|
||||
private val name: String,
|
||||
masterKey: MasterKey,
|
||||
) {
|
||||
companion object {
|
||||
private const val TAG = "DatabaseManager"
|
||||
|
||||
private val mutex = Mutex()
|
||||
}
|
||||
|
||||
private val dbFileIo = ioEffect { applicationContext.getDatabasePath(name) }
|
||||
.shared("dbFileIo")
|
||||
|
||||
private val dbIo = io(masterKey)
|
||||
.effectMap { masterKey ->
|
||||
val databaseFile = dbFileIo
|
||||
.bind()
|
||||
if (!databaseFile.exists()) {
|
||||
return@effectMap null
|
||||
}
|
||||
// Create encrypted database using the provided master key. If the
|
||||
// key is incorrect, trying to open the database would lead to a crash.
|
||||
val factory = SupportOpenHelperFactory(masterKey.byteArray, null, false)
|
||||
Room
|
||||
.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java,
|
||||
name,
|
||||
)
|
||||
.openHelperFactory(factory)
|
||||
.addTypeConverter(RoomSecretConverter(json))
|
||||
.build()
|
||||
}
|
||||
.retry { e, attempt ->
|
||||
e.printStackTrace()
|
||||
if (e is SQLException && attempt == 0) {
|
||||
applicationContext.deleteDatabase(name)
|
||||
return@retry true
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
.shared("dbIo")
|
||||
|
||||
fun migrateIfExists(
|
||||
sqld: Database,
|
||||
): IO<Unit> = ioEffect(Dispatchers.IO) {
|
||||
val room = dbIo.bind()
|
||||
// Nothing to migrate.
|
||||
?: return@ioEffect
|
||||
|
||||
// account
|
||||
MigrationLogger.log("Account") {
|
||||
val accounts = room.accountDao().getAll().first()
|
||||
sqld.transaction {
|
||||
val accountIds = sqld.accountQueries
|
||||
.getIds()
|
||||
.executeAsList()
|
||||
.toSet()
|
||||
accounts.forEach { account ->
|
||||
if (account.accountId in accountIds) {
|
||||
ignored()
|
||||
return@forEach
|
||||
}
|
||||
sqld.accountQueries.insert(
|
||||
accountId = account.accountId,
|
||||
data = account.content,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
}
|
||||
}
|
||||
// profile
|
||||
MigrationLogger.log("Profile") {
|
||||
val profiles = room.profileDao().getAll().first()
|
||||
sqld.transaction {
|
||||
val profileIds = sqld.profileQueries
|
||||
.getIds()
|
||||
.executeAsList()
|
||||
.toSet()
|
||||
profiles.forEach { profile ->
|
||||
if (profile.profileId in profileIds) {
|
||||
ignored()
|
||||
return@forEach
|
||||
}
|
||||
sqld.profileQueries.insert(
|
||||
profileId = profile.profileId,
|
||||
accountId = profile.accountId,
|
||||
data = profile.content,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
}
|
||||
}
|
||||
// meta
|
||||
MigrationLogger.log("Meta") {
|
||||
val metas = room.metaDao().getAll().first()
|
||||
sqld.transaction {
|
||||
val accountIds = sqld.metaQueries
|
||||
.getIds()
|
||||
.executeAsList()
|
||||
.toSet()
|
||||
metas.forEach { meta ->
|
||||
if (meta.accountId in accountIds) {
|
||||
ignored()
|
||||
return@forEach
|
||||
}
|
||||
sqld.metaQueries.insert(
|
||||
accountId = meta.accountId,
|
||||
data = meta.content,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
}
|
||||
}
|
||||
// folder
|
||||
MigrationLogger.log("Folder") {
|
||||
val folders = room.folderDao().getAll().first()
|
||||
sqld.transaction {
|
||||
val folderIds = sqld.folderQueries
|
||||
.getIds()
|
||||
.executeAsList()
|
||||
.toSet()
|
||||
folders.forEach { folder ->
|
||||
if (folder.folderId in folderIds) {
|
||||
ignored()
|
||||
return@forEach
|
||||
}
|
||||
sqld.folderQueries.insert(
|
||||
folderId = folder.folderId,
|
||||
accountId = folder.accountId,
|
||||
data = folder.content,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
}
|
||||
}
|
||||
// org
|
||||
MigrationLogger.log("Organization") {
|
||||
val orgs = room.organizationDao().getAll().first()
|
||||
sqld.transaction {
|
||||
val organizationIds = sqld.organizationQueries
|
||||
.getIds()
|
||||
.executeAsList()
|
||||
.toSet()
|
||||
orgs.forEach { org ->
|
||||
if (org.organizationId in organizationIds) {
|
||||
ignored()
|
||||
return@forEach
|
||||
}
|
||||
sqld.organizationQueries.insert(
|
||||
organizationId = org.organizationId,
|
||||
accountId = org.accountId,
|
||||
data = org.content,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
}
|
||||
}
|
||||
// collection
|
||||
MigrationLogger.log("Collection") {
|
||||
val collections = room.collectionDao().getAll().first()
|
||||
sqld.transaction {
|
||||
val collectionIds = sqld.collectionQueries
|
||||
.getIds()
|
||||
.executeAsList()
|
||||
.toSet()
|
||||
collections.forEach { collection ->
|
||||
if (collection.collectionId in collectionIds) {
|
||||
ignored()
|
||||
return@forEach
|
||||
}
|
||||
sqld.collectionQueries.insert(
|
||||
collectionId = collection.collectionId,
|
||||
accountId = collection.accountId,
|
||||
data = collection.content,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
}
|
||||
}
|
||||
// cipher
|
||||
MigrationLogger.log("Cipher") {
|
||||
val ciphers = room.cipherDao().getAll().first()
|
||||
sqld.transaction {
|
||||
val cipherIds = sqld.cipherQueries
|
||||
.getIds()
|
||||
.executeAsList()
|
||||
.toSet()
|
||||
ciphers.forEach { cipher ->
|
||||
if (cipher.itemId in cipherIds) {
|
||||
ignored()
|
||||
return@forEach
|
||||
}
|
||||
sqld.cipherQueries.insert(
|
||||
cipherId = cipher.itemId,
|
||||
accountId = cipher.accountId,
|
||||
folderId = cipher.folderId,
|
||||
data = cipher.content,
|
||||
updatedAt = cipher.content.revisionDate,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
}
|
||||
}
|
||||
// history
|
||||
MigrationLogger.log("History") {
|
||||
val openCiphers = room.openCipherHistoryDao().getAll().first()
|
||||
val fillCiphers = room.fillCipherHistoryDao().getAll().first()
|
||||
sqld.transaction {
|
||||
val hasExistingHistory = sqld.cipherUsageHistoryQueries
|
||||
.get(1)
|
||||
.executeAsList()
|
||||
.isNotEmpty()
|
||||
if (hasExistingHistory) {
|
||||
return@transaction
|
||||
}
|
||||
|
||||
openCiphers.forEach { openCipher ->
|
||||
sqld.cipherUsageHistoryQueries.insert(
|
||||
cipherId = openCipher.itemId,
|
||||
credentialId = null,
|
||||
createdAt = openCipher.timestamp
|
||||
.let(Instant::fromEpochMilliseconds),
|
||||
type = CipherHistoryType.OPENED,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
fillCiphers.forEach { fillCipher ->
|
||||
sqld.cipherUsageHistoryQueries.insert(
|
||||
cipherId = fillCipher.itemId,
|
||||
credentialId = null,
|
||||
createdAt = fillCipher.timestamp
|
||||
.let(Instant::fromEpochMilliseconds),
|
||||
type = CipherHistoryType.USED_AUTOFILL,
|
||||
)
|
||||
changed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val dbFile = dbFileIo.bind()
|
||||
dbFile.delete()
|
||||
}
|
||||
|
||||
private class MigrationLogger(
|
||||
private val tag: String,
|
||||
) : MigrationLoggerScope {
|
||||
companion object {
|
||||
suspend fun log(
|
||||
tag: String,
|
||||
block: suspend MigrationLoggerScope.() -> Unit,
|
||||
) {
|
||||
val logger = MigrationLogger(tag)
|
||||
block(logger)
|
||||
logger.flush()
|
||||
}
|
||||
}
|
||||
|
||||
private val now = Clock.System.now()
|
||||
|
||||
var changed = 0
|
||||
var ignored = 0
|
||||
|
||||
override fun changed() {
|
||||
changed += 1
|
||||
}
|
||||
|
||||
override fun ignored() {
|
||||
ignored += 1
|
||||
}
|
||||
|
||||
fun flush() {
|
||||
val dt = Clock.System.now() - now
|
||||
Log.i("MigrationLogger", "$tag: Changed $changed, ignored $ignored! It took $dt.")
|
||||
}
|
||||
}
|
||||
|
||||
interface MigrationLoggerScope {
|
||||
fun changed()
|
||||
|
||||
fun ignored()
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.AutoMigration
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import com.artemchep.keyguard.room.dao.AccountDao
|
||||
import com.artemchep.keyguard.room.dao.CipherDao
|
||||
import com.artemchep.keyguard.room.dao.CollectionDao
|
||||
import com.artemchep.keyguard.room.dao.FillCipherHistoryDao
|
||||
import com.artemchep.keyguard.room.dao.FolderDao
|
||||
import com.artemchep.keyguard.room.dao.MetaDao
|
||||
import com.artemchep.keyguard.room.dao.OpenCipherHistoryDao
|
||||
import com.artemchep.keyguard.room.dao.OrganizationDao
|
||||
import com.artemchep.keyguard.room.dao.ProfileDao
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
RoomBitwardenCipher::class,
|
||||
RoomBitwardenCollection::class,
|
||||
RoomBitwardenOrganization::class,
|
||||
RoomBitwardenFolder::class,
|
||||
RoomBitwardenProfile::class,
|
||||
RoomBitwardenMeta::class,
|
||||
RoomBitwardenToken::class,
|
||||
// history
|
||||
RoomOpenCipherHistory::class,
|
||||
RoomFillCipherHistory::class,
|
||||
],
|
||||
autoMigrations = [
|
||||
AutoMigration(from = 1, to = 2),
|
||||
AutoMigration(from = 2, to = 3),
|
||||
AutoMigration(from = 3, to = 4),
|
||||
AutoMigration(from = 4, to = 5),
|
||||
AutoMigration(from = 5, to = 6),
|
||||
AutoMigration(from = 6, to = 7),
|
||||
],
|
||||
version = 7,
|
||||
exportSchema = true,
|
||||
)
|
||||
@TypeConverters(RoomSecretConverter::class)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun cipherDao(): CipherDao
|
||||
abstract fun collectionDao(): CollectionDao
|
||||
abstract fun organizationDao(): OrganizationDao
|
||||
abstract fun metaDao(): MetaDao
|
||||
abstract fun folderDao(): FolderDao
|
||||
abstract fun profileDao(): ProfileDao
|
||||
abstract fun accountDao(): AccountDao
|
||||
abstract fun fillCipherHistoryDao(): FillCipherHistoryDao
|
||||
abstract fun openCipherHistoryDao(): OpenCipherHistoryDao
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenCipher
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomBitwardenCipher",
|
||||
primaryKeys = [
|
||||
"itemId",
|
||||
],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenToken::class,
|
||||
parentColumns = arrayOf("accountId"),
|
||||
childColumns = arrayOf("accountId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class RoomBitwardenCipher(
|
||||
val itemId: String,
|
||||
val accountId: String,
|
||||
val folderId: String?,
|
||||
val content: BitwardenCipher,
|
||||
)
|
|
@ -1,26 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenCollection
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomBitwardenCollection",
|
||||
primaryKeys = [
|
||||
"collectionId",
|
||||
"accountId",
|
||||
],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenToken::class,
|
||||
parentColumns = arrayOf("accountId"),
|
||||
childColumns = arrayOf("accountId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class RoomBitwardenCollection(
|
||||
val collectionId: String,
|
||||
val accountId: String,
|
||||
val content: BitwardenCollection,
|
||||
)
|
|
@ -1,25 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenFolder
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomBitwardenFolder",
|
||||
primaryKeys = [
|
||||
"folderId",
|
||||
],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenToken::class,
|
||||
parentColumns = arrayOf("accountId"),
|
||||
childColumns = arrayOf("accountId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class RoomBitwardenFolder(
|
||||
val folderId: String,
|
||||
val accountId: String,
|
||||
val content: BitwardenFolder,
|
||||
)
|
|
@ -1,24 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenMeta
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomBitwardenMeta",
|
||||
primaryKeys = [
|
||||
"accountId",
|
||||
],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenToken::class,
|
||||
parentColumns = arrayOf("accountId"),
|
||||
childColumns = arrayOf("accountId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class RoomBitwardenMeta(
|
||||
val accountId: String,
|
||||
val content: BitwardenMeta,
|
||||
)
|
|
@ -1,26 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenOrganization
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomBitwardenOrganization",
|
||||
primaryKeys = [
|
||||
"organizationId",
|
||||
"accountId",
|
||||
],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenToken::class,
|
||||
parentColumns = arrayOf("accountId"),
|
||||
childColumns = arrayOf("accountId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class RoomBitwardenOrganization(
|
||||
val organizationId: String,
|
||||
val accountId: String,
|
||||
val content: BitwardenOrganization,
|
||||
)
|
|
@ -1,26 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenProfile
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomBitwardenProfile",
|
||||
primaryKeys = [
|
||||
"profileId",
|
||||
"accountId",
|
||||
],
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenToken::class,
|
||||
parentColumns = arrayOf("accountId"),
|
||||
childColumns = arrayOf("accountId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class RoomBitwardenProfile(
|
||||
val profileId: String,
|
||||
val accountId: String,
|
||||
val content: BitwardenProfile,
|
||||
)
|
|
@ -1,15 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenToken
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomBitwardenToken",
|
||||
primaryKeys = [
|
||||
"accountId",
|
||||
],
|
||||
)
|
||||
data class RoomBitwardenToken(
|
||||
val accountId: String,
|
||||
val content: BitwardenToken,
|
||||
)
|
|
@ -1,30 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomFillCipherHistory",
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenToken::class,
|
||||
parentColumns = arrayOf("accountId"),
|
||||
childColumns = arrayOf("accountId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenCipher::class,
|
||||
parentColumns = arrayOf("itemId"),
|
||||
childColumns = arrayOf("itemId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class RoomFillCipherHistory(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Int = 0,
|
||||
val itemId: String,
|
||||
val accountId: String,
|
||||
val timestamp: Long,
|
||||
)
|
|
@ -1,5 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
class RoomMigrationException(
|
||||
e: Throwable,
|
||||
) : RuntimeException(e)
|
|
@ -1,30 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.ForeignKey
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "RoomOpenCipherHistory",
|
||||
foreignKeys = [
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenToken::class,
|
||||
parentColumns = arrayOf("accountId"),
|
||||
childColumns = arrayOf("accountId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
ForeignKey(
|
||||
entity = RoomBitwardenCipher::class,
|
||||
parentColumns = arrayOf("itemId"),
|
||||
childColumns = arrayOf("itemId"),
|
||||
onDelete = ForeignKey.CASCADE,
|
||||
),
|
||||
],
|
||||
)
|
||||
data class RoomOpenCipherHistory(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
val id: Int = 0,
|
||||
val itemId: String,
|
||||
val accountId: String,
|
||||
val timestamp: Long,
|
||||
)
|
|
@ -1,80 +0,0 @@
|
|||
package com.artemchep.keyguard.room
|
||||
|
||||
import androidx.room.ProvidedTypeConverter
|
||||
import androidx.room.TypeConverter
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenCipher
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenCollection
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenFolder
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenMeta
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenOrganization
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenProfile
|
||||
import com.artemchep.keyguard.core.store.bitwarden.BitwardenToken
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@ProvidedTypeConverter
|
||||
class RoomSecretConverter(
|
||||
private val json: Json,
|
||||
) {
|
||||
@TypeConverter
|
||||
fun encodeToken(model: BitwardenToken?) = encode(model)
|
||||
|
||||
@TypeConverter
|
||||
fun encodeCipher(model: BitwardenCipher?) = encode(model)
|
||||
|
||||
@TypeConverter
|
||||
fun encodeCollection(model: BitwardenCollection?) = encode(model)
|
||||
|
||||
@TypeConverter
|
||||
fun encodeFolder(model: BitwardenFolder?) = encode(model)
|
||||
|
||||
@TypeConverter
|
||||
fun encodeMeta(model: BitwardenMeta?) = encode(model)
|
||||
|
||||
@TypeConverter
|
||||
fun encodeProfile(model: BitwardenProfile?) = encode(model)
|
||||
|
||||
@TypeConverter
|
||||
fun encodeOrganization(model: BitwardenOrganization?) = encode(model)
|
||||
|
||||
private inline fun <reified T : Any> encode(model: T?): String? {
|
||||
if (model == null) {
|
||||
// We can not serialize null models, so just
|
||||
// write them as null.
|
||||
return null
|
||||
}
|
||||
|
||||
return json.encodeToString(model)
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun decodeToken(text: String?): BitwardenToken? = decode(text)
|
||||
|
||||
@TypeConverter
|
||||
fun decodeCipher(text: String?): BitwardenCipher? = decode(text)
|
||||
|
||||
@TypeConverter
|
||||
fun decodeCollection(text: String?): BitwardenCollection? = decode(text)
|
||||
|
||||
@TypeConverter
|
||||
fun decodeFolder(text: String?): BitwardenFolder? = decode(text)
|
||||
|
||||
@TypeConverter
|
||||
fun decodeMeta(text: String?): BitwardenMeta? = decode(text)
|
||||
|
||||
@TypeConverter
|
||||
fun decodeProfile(text: String?): BitwardenProfile? = decode(text)
|
||||
|
||||
@TypeConverter
|
||||
fun decodeOrganization(text: String?): BitwardenOrganization? = decode(text)
|
||||
|
||||
private inline fun <reified T : Any> decode(text: String?): T? {
|
||||
if (text == null) {
|
||||
// We can not serialize null models, so just
|
||||
// write them as null.
|
||||
return null
|
||||
}
|
||||
|
||||
return json.decodeFromString<T>(text)
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomBitwardenToken
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface AccountDao {
|
||||
@Query("SELECT * FROM RoomBitwardenToken")
|
||||
fun getAll(): Flow<List<RoomBitwardenToken>>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomBitwardenCipher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface CipherDao {
|
||||
@Query("SELECT * FROM RoomBitwardenCipher")
|
||||
fun getAll(): Flow<List<RoomBitwardenCipher>>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomBitwardenCollection
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface CollectionDao {
|
||||
@Query("SELECT * FROM RoomBitwardenCollection")
|
||||
fun getAll(): Flow<List<RoomBitwardenCollection>>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomFillCipherHistory
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface FillCipherHistoryDao {
|
||||
@Query("SELECT * FROM RoomFillCipherHistory")
|
||||
fun getAll(): Flow<List<RoomFillCipherHistory>>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomBitwardenFolder
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface FolderDao {
|
||||
@Query("SELECT * FROM RoomBitwardenFolder")
|
||||
fun getAll(): Flow<List<RoomBitwardenFolder>>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomBitwardenMeta
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface MetaDao {
|
||||
@Query("SELECT * FROM RoomBitwardenMeta")
|
||||
fun getAll(): Flow<List<RoomBitwardenMeta>>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomOpenCipherHistory
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface OpenCipherHistoryDao {
|
||||
@Query("SELECT * FROM RoomOpenCipherHistory ORDER BY timestamp DESC")
|
||||
fun getAll(): Flow<List<RoomOpenCipherHistory>>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomBitwardenOrganization
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface OrganizationDao {
|
||||
@Query("SELECT * FROM RoomBitwardenOrganization")
|
||||
fun getAll(): Flow<List<RoomBitwardenOrganization>>
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package com.artemchep.keyguard.room.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Query
|
||||
import com.artemchep.keyguard.room.RoomBitwardenProfile
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface ProfileDao {
|
||||
@Query("SELECT * FROM RoomBitwardenProfile")
|
||||
fun getAll(): Flow<List<RoomBitwardenProfile>>
|
||||
}
|
Loading…
Reference in New Issue