Skip to content

Commit

Permalink
Database, entities, and DAOs. Oh my!
Browse files Browse the repository at this point in the history
  • Loading branch information
SaintPatrck committed Apr 5, 2024
1 parent 9aa0918 commit 450ec3b
Show file tree
Hide file tree
Showing 22 changed files with 410 additions and 220 deletions.
5 changes: 5 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ android {
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

ksp {
// The location in which the generated Room Database Schemas will be stored in the repo.
arg("room.schemaLocation", "$projectDir/schemas")
}
}

androidResources {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "aa30fba94617d1ba82f69d7d335261d2",
"entities": [
{
"tableName": "items",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `type` TEXT NOT NULL, `period` INTEGER NOT NULL, `digits` INTEGER NOT NULL, `key` TEXT NOT NULL, `issuer` TEXT, `label` TEXT NOT NULL, `userId` TEXT, `username` TEXT, PRIMARY KEY(`id`))",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "period",
"columnName": "period",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "digits",
"columnName": "digits",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "issuer",
"columnName": "issuer",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userId",
"columnName": "userId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "username",
"columnName": "username",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'aa30fba94617d1ba82f69d7d335261d2')"
]
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk

import com.bitwarden.core.CipherView
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemEntity
import kotlinx.coroutines.flow.Flow

/**
Expand All @@ -9,18 +9,18 @@ import kotlinx.coroutines.flow.Flow
interface AuthenticatorDiskSource {

/**
* Saves a cipher to the data source.
* Saves an authenticator item to the data source.
*/
suspend fun saveCipher(cipher: CipherView)
suspend fun saveItem(authenticatorItem: AuthenticatorItemEntity)

/**
* Retrieves all ciphers from the data source.
* Retrieves all authenticator items from the data source.
*/
fun getCiphers(): Flow<List<CipherView>>
fun getItems(): Flow<List<AuthenticatorItemEntity>>

/**
* Deletes a cipher from the data source for the given [cipherId].
* Deletes an authenticator item from the data source with the given [itemId].
*/
suspend fun deleteCipher(cipherId: String)
suspend fun deleteItem(itemId: String)

}
Original file line number Diff line number Diff line change
@@ -1,99 +1,28 @@
package com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk

import com.bitwarden.core.CipherView
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.dao.ItemDao
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemEntity
import com.x8bit.bitwarden.authenticator.data.platform.repository.util.bufferedMutableSharedFlow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
import javax.inject.Inject

class AuthenticatorDiskSourceImpl @Inject constructor() : AuthenticatorDiskSource {
class AuthenticatorDiskSourceImpl @Inject constructor(
private val itemDao: ItemDao,
) : AuthenticatorDiskSource {

private val ciphers = STATIC_CIPHER_CACHE.toMutableList()
private val mutableCiphersFlow = bufferedMutableSharedFlow<List<CipherView>>()
private val forceItemsFlow = bufferedMutableSharedFlow<List<AuthenticatorItemEntity>>()

override suspend fun saveCipher(cipher: CipherView) {
ciphers.add(cipher)
mutableCiphersFlow.tryEmit(ciphers)
override suspend fun saveItem(authenticatorItem: AuthenticatorItemEntity) {
itemDao.insert(authenticatorItem)
}

override fun getCiphers(): Flow<List<CipherView>> {
return flowOf(ciphers)
}
override fun getItems(): Flow<List<AuthenticatorItemEntity>> = merge(
forceItemsFlow,
itemDao.getAllItems()
)

override suspend fun deleteCipher(cipherId: String) {
ciphers.removeIf { it.id == cipherId }
mutableCiphersFlow.tryEmit(ciphers)
override suspend fun deleteItem(itemId: String) {
itemDao.deleteItem(itemId)
}
}

private val STATIC_CIPHER_CACHE = emptyList<CipherView>()
// listOf(
// CipherView(
// id = "1",
// organizationId = null,
// folderId = null,
// collectionIds = emptyList(),
// key = null,
// name = "TOTP test 1",
// notes = null,
// type = CipherType.LOGIN,
// login = LoginView(
// null,
// null,
// null,
// null,
// "JBSWY3DPEHPK3PXP",
// null,
// null
// ),
// identity = null,
// card = null,
// secureNote = null,
// favorite = true,
// reprompt = CipherRepromptType.NONE,
// organizationUseTotp = false,
// edit = false,
// viewPassword = true,
// localData = null,
// attachments = null,
// fields = null,
// passwordHistory = null,
// creationDate = DateTime.now(),
// deletedDate = null,
// revisionDate = DateTime.now()
// ),
// CipherView(
// id = "2",
// organizationId = null,
// folderId = null,
// collectionIds = emptyList(),
// key = null,
// name = "TOTP test 2",
// notes = null,
// type = CipherType.LOGIN,
// login = LoginView(
// null,
// null,
// null,
// null,
// "JBSWY3DPEHPK3PXP",
// null,
// null
// ),
// identity = null,
// card = null,
// secureNote = null,
// favorite = true,
// reprompt = CipherRepromptType.NONE,
// organizationUseTotp = false,
// edit = false,
// viewPassword = true,
// localData = null,
// attachments = null,
// fields = null,
// passwordHistory = null,
// creationDate = DateTime.now(),
// deletedDate = null,
// revisionDate = DateTime.now()
// )
//)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.convertor

import androidx.room.ProvidedTypeConverter
import androidx.room.TypeConverter
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemType

/**
* A [TypeConverter] to convert [AuthenticatorItemType] to and from a [String].
*/
@ProvidedTypeConverter
class AuthenticatorItemTypeConverter {

/**
* A [TypeConverter] to convert an [AuthenticatorItemType] to a [String].
*/
@TypeConverter
fun toString(item: AuthenticatorItemType): String = item.name

/**
* A [TypeConverter] to convert a [String] to an [AuthenticatorItemType].
*/
@TypeConverter
fun fromString(itemName: String) = AuthenticatorItemType
.entries
.find { it.name == itemName }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemEntity
import kotlinx.coroutines.flow.Flow

/**
* Provides methods for inserting, reading, and deleting authentication items from the database
* using [AuthenticatorItemEntity].
*/
@Dao
interface ItemDao {

/**
* Inserts a single authenticator item into the database.
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(item: AuthenticatorItemEntity)

/**
* Read all authenticator items from the database.
*/
@Query("SELECT * FROM items")
fun getAllItems(): Flow<List<AuthenticatorItemEntity>>

/**
* Deletes the specified authenticator item with the given [itemId]. This will return the number
* of rows deleted by this query.
*/
@Query("DELETE FROM items WHERE id = :itemId")
suspend fun deleteItem(itemId: String): Int
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.database

import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.convertor.AuthenticatorItemTypeConverter
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.dao.ItemDao
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.entity.AuthenticatorItemEntity

/**
* Room database for storing any persisted data.
*/
@Database(
entities = [
AuthenticatorItemEntity::class
],
version = 1,
exportSchema = true,
)
@TypeConverters(AuthenticatorItemTypeConverter::class)
abstract class AuthenticatorDatabase : RoomDatabase() {

/**
* Provide the DAO for accessing authenticator item data.
*/
abstract fun itemDao(): ItemDao

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.di

import android.app.Application
import androidx.room.Room
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.AuthenticatorDiskSource
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.AuthenticatorDiskSourceImpl
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.convertor.AuthenticatorItemTypeConverter
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.dao.ItemDao
import com.x8bit.bitwarden.authenticator.data.authenticator.datasource.disk.database.AuthenticatorDatabase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
Expand All @@ -17,6 +22,23 @@ object AuthenticatorDiskModule {

@Provides
@Singleton
fun provideAuthenticatorDiskSource(): AuthenticatorDiskSource = AuthenticatorDiskSourceImpl()
fun provideAuthenticatorDatabase(app: Application): AuthenticatorDatabase =
Room
.databaseBuilder(
context = app,
klass = AuthenticatorDatabase::class.java,
name = "authenticator_database"
)
.fallbackToDestructiveMigration()
.addTypeConverter(AuthenticatorItemTypeConverter())
.build()

@Provides
@Singleton
fun provideItemDao(database: AuthenticatorDatabase) = database.itemDao()

@Provides
@Singleton
fun provideAuthenticatorDiskSource(itemDao: ItemDao): AuthenticatorDiskSource =
AuthenticatorDiskSourceImpl(itemDao = itemDao)
}
Loading

0 comments on commit 450ec3b

Please sign in to comment.