VersionsRepo

This commit is contained in:
InsanusMokrassar 2020-11-22 19:07:37 +06:00
parent 30d1453a12
commit c40f0fdcb9
8 changed files with 251 additions and 0 deletions

View File

@ -2,6 +2,13 @@
## 0.4.4 ## 0.4.4
* `Repos`:
* Add interface `VersionsRepo`
* Add default realization of `VersionsRepo` named `StandardVersionsRepo` which use `StandardVersionsRepoProxy`
to get access to some end-store
* Add default realization of `StandardVersionsRepoProxy` based on `KeyValue` repos
* Add realizations of `StandardVersionsRepoProxy` for exposed and android (`SQL` and `SharedPreferences`)
## 0.4.3 ## 0.4.3
* `Versions`: * `Versions`:

View File

@ -0,0 +1,13 @@
package dev.inmo.micro_utils.repos.versions
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.set
class KeyValueBasedVersionsRepoProxy<T>(
private val keyValueStore: StandardKeyValueRepo<String, Int>,
override val database: T
) : StandardVersionsRepoProxy<T> {
override suspend fun getTableVersion(tableName: String): Int? = keyValueStore.get(tableName)
override suspend fun updateTableVersion(tableName: String, version: Int) { keyValueStore.set(tableName, version) }
}

View File

@ -0,0 +1,36 @@
package dev.inmo.micro_utils.repos.versions
import dev.inmo.micro_utils.repos.Repo
interface StandardVersionsRepoProxy<T> : Repo {
val database: T
suspend fun getTableVersion(tableName: String): Int?
suspend fun updateTableVersion(tableName: String, version: Int)
}
class StandardVersionsRepo<T>(
private val proxy: StandardVersionsRepoProxy<T>
) : VersionsRepo<T> {
override suspend fun setTableVersion(
tableName: String,
version: Int,
onCreate: suspend T.() -> Unit,
onUpdate: suspend T.(from: Int, to: Int) -> Unit
) {
var savedVersion = proxy.getTableVersion(tableName)
if (savedVersion == null) {
proxy.database.onCreate()
proxy.updateTableVersion(tableName, version)
} else {
while (savedVersion != null && savedVersion < version) {
val newVersion = savedVersion + 1
proxy.database.onUpdate(savedVersion, newVersion)
proxy.updateTableVersion(tableName, newVersion)
savedVersion = newVersion
}
}
}
}

View File

@ -0,0 +1,31 @@
package dev.inmo.micro_utils.repos.versions
import dev.inmo.micro_utils.repos.Repo
/**
* This interface has been created due to requirement to work with different versions of databases and make some
* migrations between versions
*
* @param T It is a type of database, which will be used by this repo to retrieve current table version and update it
*/
interface VersionsRepo<T> : Repo {
/**
* By default, instance of this interface will check that version of table with name [tableName] is less than
* [version] or is absent
*
* * In case if [tableName] didn't found, will be called [onCreate] and version of table will be set up to [version]
* * In case if [tableName] have version less than parameter [version], it will increase version one-by-one
* until database version will be equal to [version]
*
* @param version Current version of table
* @param onCreate This callback will be called in case when table have no information about table
* @param onUpdate This callback will be called after **iterative** changing of version. It is expected that parameter
* "to" will always be greater than "from"
*/
suspend fun setTableVersion(
tableName: String,
version: Int,
onCreate: suspend T.() -> Unit,
onUpdate: suspend T.(from: Int, to: Int) -> Unit
)
}

View File

@ -5,6 +5,7 @@ import android.database.DatabaseErrorHandler
import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper import android.database.sqlite.SQLiteOpenHelper
import dev.inmo.micro_utils.coroutines.safely import dev.inmo.micro_utils.coroutines.safely
import dev.inmo.micro_utils.repos.versions.*
import kotlin.coroutines.Continuation import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
@ -36,6 +37,9 @@ class StandardSQLHelper(
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {} override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {}
} }
val versionsRepo: VersionsRepo<SQLiteOpenHelper> by lazy {
StandardVersionsRepo(AndroidSQLStandardVersionsRepoProxy(sqlOpenHelper))
}
suspend fun <T> writableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.writableTransaction(block) suspend fun <T> writableTransaction(block: suspend SQLiteDatabase.() -> T): T = sqlOpenHelper.writableTransaction(block)

View File

@ -0,0 +1,63 @@
package dev.inmo.micro_utils.repos.versions
import android.database.sqlite.SQLiteOpenHelper
import androidx.core.content.contentValuesOf
import dev.inmo.micro_utils.repos.*
import kotlinx.coroutines.runBlocking
/**
* Will create [VersionsRepo] based on [SQLiteOpenHelper] with table inside of [database]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun versionsRepo(database: SQLiteOpenHelper): VersionsRepo<SQLiteOpenHelper> = StandardVersionsRepo(
AndroidSQLStandardVersionsRepoProxy(database)
)
class AndroidSQLStandardVersionsRepoProxy(
override val database: SQLiteOpenHelper
) : StandardVersionsRepoProxy<SQLiteOpenHelper> {
private val tableName: String = "AndroidSQLStandardVersionsRepo"
private val tableNameColumnName = "tableName"
private val tableVersionColumnName = "version"
init {
runBlocking(DatabaseCoroutineContext) {
database.writableTransaction {
createTable(
tableName,
tableNameColumnName to ColumnType.Text.NOT_NULLABLE,
tableVersionColumnName to ColumnType.Numeric.INTEGER()
)
}
}
}
override suspend fun getTableVersion(tableName: String): Int? = database.writableTransaction {
select(
tableName,
selection = "$tableNameColumnName=?",
selectionArgs = arrayOf(tableName),
limit = limitClause(1)
).use {
if (it.moveToFirst()) {
it.getInt(tableVersionColumnName)
} else {
null
}
}
}
override suspend fun updateTableVersion(tableName: String, version: Int) {
database.writableTransaction {
val updated = update(
tableName,
contentValuesOf(tableVersionColumnName to version),
"$tableNameColumnName=?",
arrayOf(version.toString())
) > 0
if (!updated) {
insert(tableName, null, contentValuesOf(tableNameColumnName to tableName, tableVersionColumnName to version))
}
}
}
}

View File

@ -0,0 +1,49 @@
@file:Suppress("NOTHING_TO_INLINE")
package dev.inmo.micro_utils.repos.versions
import android.content.Context
import android.database.sqlite.SQLiteOpenHelper
import androidx.core.content.contentValuesOf
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.keyvalue.keyValueStore
import kotlinx.coroutines.runBlocking
/**
* Will create [VersionsRepo] based on [T], but versions will be stored in [StandardKeyValueRepo]
*
* @receiver Will be used to create [KeyValueBasedVersionsRepoProxy] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
* @see [KeyValueBasedVersionsRepoProxy]
* @see [keyValueStore]
*/
inline fun <T> Context.versionsKeyValueRepo(
database: T
): VersionsRepo<T> = StandardVersionsRepo(
KeyValueBasedVersionsRepoProxy(
keyValueStore("SPVersionsRepo"),
database
)
)
/**
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
*
* @receiver Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
* @see [keyValueStore]
*/
inline fun Context.versionsKeyValueRepoForSQL(
database: SQLiteOpenHelper
) = versionsKeyValueRepo(database)
/**
* Will create [VersionsRepo] based on [SQLiteOpenHelper], but versions will be stored in [StandardKeyValueRepo]
*
* @param context Will be used to create [StandardKeyValueRepo] via [keyValueStore] and pass it to [StandardVersionsRepo]
*
* @see [keyValueStore]
*/
inline fun versionsRepo(
context: Context,
database: SQLiteOpenHelper
) = context.versionsKeyValueRepoForSQL(database)

View File

@ -0,0 +1,48 @@
package dev.inmo.micro_utils.repos.exposed.versions
import dev.inmo.micro_utils.repos.exposed.ExposedRepo
import dev.inmo.micro_utils.repos.exposed.initTable
import dev.inmo.micro_utils.repos.versions.*
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
/**
* Use this method to create [StandardVersionsRepo] based on [Database] with [ExposedStandardVersionsRepoProxy] as
* [StandardVersionsRepoProxy]
*/
@Suppress("NOTHING_TO_INLINE")
inline fun versionsRepo(database: Database): VersionsRepo<Database> = StandardVersionsRepo(
ExposedStandardVersionsRepoProxy(database)
)
class ExposedStandardVersionsRepoProxy(
override val database: Database
) : StandardVersionsRepoProxy<Database>, Table("ExposedVersionsProxy"), ExposedRepo {
private val tableNameColumn = text("tableName")
private val tableVersionColumn = integer("tableName")
init {
initTable()
}
override suspend fun getTableVersion(tableName: String): Int? = transaction(database) {
select { tableNameColumn.eq(tableName) }.limit(1).firstOrNull() ?.getOrNull(tableVersionColumn)
}
override suspend fun updateTableVersion(tableName: String, version: Int) {
transaction(database) {
val updated = update(
{ tableNameColumn.eq(tableName) }
) {
it[tableVersionColumn] = version
} > 0
if (!updated) {
insert {
it[tableNameColumn] = tableName
it[tableVersionColumn] = version
}
}
}
}
}