add repos ktor and exposed

This commit is contained in:
000Sanya 2020-09-22 11:47:01 +10:00
parent dc9529310c
commit b84367f47c
39 changed files with 1369 additions and 0 deletions

View File

@ -0,0 +1,16 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply from: "$mppJavaProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api internalProject("micro_utils.repos.common")
}
}
}
}

View File

@ -0,0 +1,13 @@
package dev.inmo.micro_utils.repos.exposed
import kotlinx.coroutines.channels.Channel
abstract class AbstractExposedCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = Channel.BUFFERED,
databaseName: String = ""
) :
AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize,
databaseName
),
ExposedCRUDRepo<ObjectType, IdType>

View File

@ -0,0 +1,37 @@
package dev.inmo.micro_utils.repos.exposed
import com.insanusmokrassar.postssystem.exposed.commons.paginate
import com.insanusmokrassar.postssystem.utils.common.pagination.*
import com.insanusmokrassar.postssystem.utils.repos.ReadStandardCRUDRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedReadCRUDRepo<ObjectType, IdType>(
tableName: String
) :
ReadStandardCRUDRepo<ObjectType, IdType>,
ExposedCRUDRepo<ObjectType, IdType>,
Table(tableName)
{
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> {
return transaction(db = database) {
selectAll().paginate(pagination).map {
it.asObject
}.createPaginationResult(
pagination,
selectAll().count()
)
}
}
override suspend fun getById(id: IdType): ObjectType? {
return transaction(db = database) {
select {
selectById(id)
}.limit(1).firstOrNull() ?.asObject
}
}
override suspend fun contains(id: IdType): Boolean = transaction(db = database) {
select { selectById(id) }.limit(1).any()
}
}

View File

@ -0,0 +1,105 @@
package dev.inmo.micro_utils.repos.exposed
import com.insanusmokrassar.postssystem.utils.repos.UpdatedValuePair
import com.insanusmokrassar.postssystem.utils.repos.WriteStandardCRUDRepo
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.statements.InsertStatement
import org.jetbrains.exposed.sql.statements.UpdateStatement
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
flowsChannelsSize: Int = 64,
databaseName: String = ""
) :
AbstractExposedReadCRUDRepo<ObjectType, IdType>(databaseName),
ExposedCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
{
protected val newObjectsChannel = BroadcastChannel<ObjectType>(flowsChannelsSize)
protected val updateObjectsChannel = BroadcastChannel<ObjectType>(flowsChannelsSize)
protected val deleteObjectsIdsChannel = BroadcastChannel<IdType>(flowsChannelsSize)
override val newObjectsFlow: Flow<ObjectType> = newObjectsChannel.asFlow()
override val updatedObjectsFlow: Flow<ObjectType> = updateObjectsChannel.asFlow()
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asFlow()
abstract val InsertStatement<Number>.asObject: ObjectType
abstract val selectByIds: SqlExpressionBuilder.(List<out IdType>) -> Op<Boolean>
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
protected open suspend fun onBeforeCreate(value: List<InputValueType>) {}
private fun createWithoutNotification(value: InputValueType): ObjectType {
return transaction(database) {
insert { insert(value, it) }.asObject
}
}
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
onBeforeCreate(values)
return transaction(db = database) {
values.map { value -> createWithoutNotification(value) }
}.also {
it.forEach {
newObjectsChannel.send(it)
}
}
}
protected open suspend fun onBeforeUpdate(value: List<UpdatedValuePair<IdType, InputValueType>>) {}
private fun updateWithoutNotification(id: IdType, value: InputValueType): ObjectType? {
return transaction(db = database) {
update(
{
selectById(this, id)
}
) {
update(id, value, it)
}
}.let {
if (it > 0) {
select {
selectById(this, id)
}.limit(1).firstOrNull() ?.asObject
} else {
null
}
}
}
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
onBeforeUpdate(listOf(id to value))
return updateWithoutNotification(id, value).also {
if (it != null) {
updateObjectsChannel.send(it)
}
}
}
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
onBeforeUpdate(values)
return (
transaction(db = database) {
values.map { (id, value) -> updateWithoutNotification(id, value) }
}.filter {
it != null
} as List<ObjectType>
).also {
it.forEach {
updateObjectsChannel.send(it)
}
}
}
protected open suspend fun onBeforeDelete(ids: List<IdType>) {}
override suspend fun deleteById(ids: List<IdType>) {
onBeforeDelete(ids)
transaction(db = database) {
deleteWhere(null, null) {
selectByIds(ids)
}
}
}
}

View File

@ -0,0 +1,10 @@
package dev.inmo.micro_utils.repos.exposed
import org.jetbrains.exposed.sql.*
interface ExposedCRUDRepo<ObjectType, IdType> {
val database: Database
val ResultRow.asObject: ObjectType
val selectById: SqlExpressionBuilder.(IdType) -> Op<Boolean>
}

View File

@ -0,0 +1,8 @@
package dev.inmo.micro_utils.repos.exposed
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
fun Table.initTable(database: Database) {
transaction(database) { SchemaUtils.createMissingTablesAndColumns(this@initTable) }
}

View File

@ -0,0 +1,48 @@
package dev.inmo.micro_utils.repos.exposed.keyvalue
import com.insanusmokrassar.postssystem.utils.repos.StandardKeyValueRepo
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.asFlow
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedKeyValueRepo<Key, Value>(
database: Database,
keyColumn: Column<Key>,
valueColumn: Column<Value>
) : StandardKeyValueRepo<Key, Value>, AbstractExposedReadKeyValueRepo<Key, Value>(
database,
keyColumn,
valueColumn
) {
private val onNewValueChannel = BroadcastChannel<Pair<Key, Value>>(Channel.BUFFERED)
private val onValueRemovedChannel = BroadcastChannel<Key>(Channel.BUFFERED)
override val onNewValue: Flow<Pair<Key, Value>> = onNewValueChannel.asFlow()
override val onValueRemoved: Flow<Key> = onValueRemovedChannel.asFlow()
override suspend fun set(k: Key, v: Value) {
transaction(db = database) {
if (select { keyColumn.eq(k) }.limit(1).any()) {
update({ keyColumn.eq(k) }) {
it[valueColumn] = v
}
} else {
insert {
it[keyColumn] = k
it[valueColumn] = v
}
}
}
onNewValueChannel.send(k to v)
}
override suspend fun unset(k: Key) {
transaction(db = database) {
deleteWhere { keyColumn.eq(k) }
}
onValueRemovedChannel.send(k)
}
}

View File

@ -0,0 +1,37 @@
package dev.inmo.micro_utils.repos.exposed.keyvalue
import com.insanusmokrassar.postssystem.exposed.commons.paginate
import com.insanusmokrassar.postssystem.utils.common.pagination.*
import com.insanusmokrassar.postssystem.utils.repos.StandardReadKeyValueRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractExposedReadKeyValueRepo<Key, Value>(
protected val database: Database,
protected val keyColumn: Column<Key>,
protected val valueColumn: Column<Value>
) : StandardReadKeyValueRepo<Key, Value>, Table() {
override val primaryKey: PrimaryKey = PrimaryKey(keyColumn, valueColumn)
override suspend fun get(k: Key): Value? = transaction(db = database) {
select { keyColumn.eq(k) }.limit(1).firstOrNull() ?.getOrNull(valueColumn)
}
override suspend fun contains(key: Key): Boolean = transaction(db = database) {
select { keyColumn.eq(key) }.limit(1).any()
}
override suspend fun count(): Long = transaction(db = database) { selectAll().count() }
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(db = database) {
selectAll().paginate(pagination, keyColumn to if (reversed) SortOrder.DESC else SortOrder.ASC).map {
it[keyColumn]
}
}.createPaginationResult(pagination, count())
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = transaction(db = database) {
selectAll().paginate(pagination, keyColumn to if (reversed) SortOrder.DESC else SortOrder.ASC).map {
it[valueColumn]
}
}.createPaginationResult(pagination, count())
}

View File

@ -0,0 +1,34 @@
package dev.inmo.micro_utils.repos.exposed.onetomany
import com.insanusmokrassar.budgetmanager.core.utils.repo.onetomany.AbstractOneToManyExposedReadKeyValueRepo
import com.insanusmokrassar.budgetmanager.core.utils.repo.onetomany.ColumnAllocator
import com.insanusmokrassar.postssystem.utils.repos.OneToManyKeyValueRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
abstract class AbstractOneToManyExposedKeyValueRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
database: Database
) : OneToManyKeyValueRepo<Key, Value>, AbstractOneToManyExposedReadKeyValueRepo<Key, Value>(
keyColumnAllocator,
valueColumnAllocator,
database
) {
override suspend fun add(k: Key, v: Value) {
transaction(db = database) {
insert {
it[keyColumn] = k
it[valueColumn] = v
}
}
}
override suspend fun remove(k: Key, v: Value) {
transaction(db = database) { deleteWhere { keyColumn.eq(k).and(valueColumn.eq(v)) } }
}
override suspend fun clear(k: Key) {
transaction(db = database) { deleteWhere { keyColumn.eq(k) } }
}
}

View File

@ -0,0 +1,48 @@
package dev.inmo.micro_utils.repos.exposed.onetomany
import com.insanusmokrassar.postssystem.exposed.commons.paginate
import com.insanusmokrassar.postssystem.utils.common.pagination.*
import com.insanusmokrassar.postssystem.utils.repos.OneToManyReadKeyValueRepo
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
typealias ColumnAllocator<T> = Table.() -> Column<T>
abstract class AbstractOneToManyExposedReadKeyValueRepo<Key, Value>(
keyColumnAllocator: ColumnAllocator<Key>,
valueColumnAllocator: ColumnAllocator<Value>,
protected val database: Database
) : OneToManyReadKeyValueRepo<Key, Value>, Table() {
protected val keyColumn: Column<Key> = keyColumnAllocator()
protected val valueColumn: Column<Value> = valueColumnAllocator()
override suspend fun count(k: Key): Long = transaction(db = database) { select { keyColumn.eq(k) }.count() }
override suspend fun count(): Long = transaction(db = database) { selectAll().count() }
override suspend fun get(
k: Key,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Value> = transaction(db = database) {
select { keyColumn.eq(k) }.paginate(pagination, keyColumn, reversed).map { it[valueColumn] }
}.createPaginationResult(
pagination,
count(k)
)
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = transaction(db = database) {
selectAll().paginate(pagination, keyColumn, reversed).map { it[keyColumn] }
}.createPaginationResult(
pagination,
count()
)
override suspend fun contains(k: Key): Boolean = transaction(db = database) {
select { keyColumn.eq(k) }.limit(1).any()
}
override suspend fun contains(k: Key, v: Value): Boolean = transaction(db = database) {
select { keyColumn.eq(k).and(valueColumn.eq(v)) }.limit(1).any()
}
}

View File

@ -0,0 +1,17 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api internalProject("micro_utils.repos.ktor.common")
api "io.ktor:ktor-client:$ktor_version"
}
}
}
}

View File

@ -0,0 +1,46 @@
package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
class KtorReadStandardCrudRepo<ObjectType, IdType> (
private val baseUrl: String,
private val client: HttpClient = HttpClient(),
private val objectsSerializer: KSerializer<ObjectType>,
private val objectsSerializerNullable: KSerializer<ObjectType?>,
private val idsSerializer: KSerializer<IdType>
) : ReadStandardCRUDRepo<ObjectType, IdType> {
private val paginationResultSerializer = PaginationResult.serializer(objectsSerializer)
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = client.uniget(
buildStandardUrl(baseUrl, getByPaginationRouting, pagination.asUrlQueryParts),
paginationResultSerializer
)
override suspend fun getById(id: IdType): ObjectType? = client.uniget(
buildStandardUrl(
baseUrl,
getByIdRouting,
mapOf(
"id" to idsSerializer.encodeUrlQueryValue(id)
)
),
objectsSerializerNullable
)
override suspend fun contains(id: IdType): Boolean = client.uniget(
buildStandardUrl(
baseUrl,
containsRouting,
mapOf(
"id" to idsSerializer.encodeUrlQueryValue(id)
)
),
Boolean.serializer()
)
}

View File

@ -0,0 +1,30 @@
package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
class KtorStandardCrudRepo<ObjectType, IdType, InputValue> (
baseUrl: String,
baseSubpart: String,
client: HttpClient,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>
) : StandardCRUDRepo<ObjectType, IdType, InputValue>,
ReadStandardCRUDRepo<ObjectType, IdType> by KtorReadStandardCrudRepo(
"$baseUrl/$baseSubpart",
client,
objectsSerializer,
objectsNullableSerializer,
idsSerializer
),
WriteStandardCRUDRepo<ObjectType, IdType, InputValue> by KtorWriteStandardCrudRepo(
"$baseUrl/$baseSubpart",
client,
objectsSerializer,
objectsNullableSerializer,
inputsSerializer,
idsSerializer
)

View File

@ -0,0 +1,65 @@
package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.repos.UpdatedValuePair
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.client.HttpClient
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
private val baseUrl: String,
private val client: HttpClient = HttpClient(),
private val objectsSerializer: KSerializer<ObjectType>,
private val objectsNullableSerializer: KSerializer<ObjectType?>,
private val inputsSerializer: KSerializer<InputValue>,
private val idsSerializer: KSerializer<IdType>
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValue> {
private val listObjectsSerializer = ListSerializer(objectsSerializer)
private val listInputSerializer = ListSerializer(inputsSerializer)
private val listIdsSerializer = ListSerializer(idsSerializer)
private val inputUpdateSerializer = PairSerializer(
idsSerializer,
inputsSerializer
)
private val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer)
override val newObjectsFlow: Flow<ObjectType> = client.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, newObjectsFlowRouting),
deserializer = objectsSerializer
)
override val updatedObjectsFlow: Flow<ObjectType> = client.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, updatedObjectsFlowRouting),
deserializer = objectsSerializer
)
override val deletedObjectsIdsFlow: Flow<IdType> = client.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, deletedObjectsIdsFlowRouting),
deserializer = idsSerializer
)
override suspend fun create(values: List<InputValue>): List<ObjectType> = client.unipost(
buildStandardUrl(baseUrl, createRouting),
BodyPair(listInputSerializer, values),
listObjectsSerializer
)
override suspend fun update(id: IdType, value: InputValue): ObjectType? = client.unipost(
buildStandardUrl(baseUrl, updateRouting),
BodyPair(inputUpdateSerializer, id to value),
objectsNullableSerializer
)
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValue>>): List<ObjectType> = client.unipost(
buildStandardUrl(baseUrl, updateManyRouting),
BodyPair(listInputUpdateSerializer, values),
listObjectsSerializer
)
override suspend fun deleteById(ids: List<IdType>) = client.unipost(
buildStandardUrl(baseUrl, deleteByIdRouting),
BodyPair(listIdsSerializer, ids),
Unit.serializer()
)
}

View File

@ -0,0 +1,29 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.StandardReadKeyValueRepo
import dev.inmo.micro_utils.repos.StandardWriteKeyValueRepo
import io.ktor.client.*
import kotlinx.serialization.KSerializer
class KtorStandartKeyValueRepo<K, V> (
baseUrl: String,
baseSubpart: String,
client: HttpClient = HttpClient(),
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>
) : StandardKeyValueRepo<K, V>,
StandardReadKeyValueRepo<K, V> by KtorStandartReadKeyValueRepo(
"$baseUrl/$baseSubpart",
client,
keySerializer,
valueSerializer,
valueNullableSerializer
),
StandardWriteKeyValueRepo<K, V> by KtorStandartWriteKeyValueRepo(
"$baseUrl/$baseSubpart",
client,
keySerializer,
valueSerializer
)

View File

@ -0,0 +1,69 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.StandardReadKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
class KtorStandartReadKeyValueRepo<Key, Value> (
private var baseUrl: String,
private var client: HttpClient = HttpClient(),
private var keySerializer: KSerializer<Key>,
private var valueSerializer: KSerializer<Value>,
private var valueNullableSerializer: KSerializer<Value?>,
) : StandardReadKeyValueRepo<Key, Value> {
override suspend fun get(k: Key): Value? = client.uniget(
buildStandardUrl(
baseUrl,
getRoute,
mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(k)
)
),
valueNullableSerializer
)
override suspend fun values(pagination: Pagination, reversed: Boolean): PaginationResult<Value> = client.uniget(
buildStandardUrl(
baseUrl,
valuesRoute,
mapOf(
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed)
) + pagination.asUrlQueryParts
),
PaginationResult.serializer(valueSerializer)
)
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = client.uniget(
buildStandardUrl(
baseUrl,
keysRoute,
mapOf(
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed)
) + pagination.asUrlQueryParts
),
PaginationResult.serializer(keySerializer)
)
override suspend fun contains(key: Key): Boolean = client.uniget(
buildStandardUrl(
baseUrl,
containsRoute,
mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(key)
),
),
Boolean.serializer(),
)
override suspend fun count(): Long = client.uniget(
buildStandardUrl(
baseUrl,
countRoute,
),
Long.serializer()
)
}

View File

@ -0,0 +1,44 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.repos.StandardWriteKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.*
import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
class KtorStandartWriteKeyValueRepo<K, V> (
private var baseUrl: String,
private var client: HttpClient = HttpClient(),
private var keySerializer: KSerializer<K>,
private var valueSerializer: KSerializer<V>,
) : StandardWriteKeyValueRepo<K, V> {
override val onNewValue: Flow<Pair<K, V>> = client.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute),
deserializer = PairSerializer(keySerializer, valueSerializer)
)
override val onValueRemoved: Flow<K> = client.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onValueRemovedRoute),
deserializer = keySerializer
)
override suspend fun set(k: K, v: V) = client.unipost(
buildStandardUrl(
baseUrl,
setRoute
),
BodyPair(KeyValuePostObject.serializer(keySerializer, valueSerializer), KeyValuePostObject(k, v)),
Unit.serializer()
)
override suspend fun unset(k: K) = client.unipost(
buildStandardUrl(
baseUrl,
unsetRoute,
),
BodyPair(keySerializer, k),
Unit.serializer()
)
}

View File

@ -0,0 +1,27 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.OneToManyReadKeyValueRepo
import dev.inmo.micro_utils.repos.OneToManyWriteKeyValueRepo
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
class KtorOneToManyKeyValueRepo<Key, Value>(
baseUrl: String,
baseSubpart: String,
client: HttpClient,
keySerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
) : OneToManyKeyValueRepo<Key, Value>,
OneToManyReadKeyValueRepo<Key, Value> by KtorOneToManyReadKeyValueRepo<Key, Value> (
"$baseUrl/$baseSubpart",
client,
keySerializer,
valueSerializer,
),
OneToManyWriteKeyValueRepo<Key, Value> by KtorOneToManyWriteKeyValueRepo<Key, Value> (
"$baseUrl/$baseSubpart",
client,
keySerializer,
valueSerializer,
)

View File

@ -0,0 +1,83 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.pagination.Pagination
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.OneToManyReadKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
class KtorOneToManyReadKeyValueRepo<Key, Value> (
private val baseUrl: String,
private val client: HttpClient = HttpClient(),
private val keySerializer: KSerializer<Key>,
private val valueSerializer: KSerializer<Value>,
) : OneToManyReadKeyValueRepo<Key, Value> {
private val paginationValueResultSerializer = PaginationResult.serializer(valueSerializer)
private val paginationKeyResultSerializer = PaginationResult.serializer(keySerializer)
override suspend fun get(k: Key, pagination: Pagination, reversed: Boolean): PaginationResult<Value> = client.uniget(
buildStandardUrl(
baseUrl,
getRoute,
mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(k),
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed)
) + pagination.asUrlQueryParts
),
paginationValueResultSerializer
)
override suspend fun keys(pagination: Pagination, reversed: Boolean): PaginationResult<Key> = client.uniget(
buildStandardUrl(
baseUrl,
keysRoute,
mapOf(
reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed)
) + pagination.asUrlQueryParts
),
paginationKeyResultSerializer
)
override suspend fun contains(k: Key): Boolean = client.uniget(
buildStandardUrl(
baseUrl,
containsByKeyRoute,
mapOf(keyParameterName to keySerializer.encodeUrlQueryValue(k))
),
Boolean.serializer()
)
override suspend fun contains(k: Key, v: Value): Boolean = client.uniget(
buildStandardUrl(
baseUrl,
containsByKeyValueRoute,
mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(k),
valueParameterName to valueSerializer.encodeUrlQueryValue(v),
)
),
Boolean.serializer()
)
override suspend fun count(k: Key): Long = client.uniget(
buildStandardUrl(
baseUrl,
countByKeyRoute,
mapOf(
keyParameterName to keySerializer.encodeUrlQueryValue(k)
)
),
Long.serializer()
)
override suspend fun count(): Long = client.uniget(
buildStandardUrl(
baseUrl,
countRoute,
),
Long.serializer()
)
}

View File

@ -0,0 +1,44 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.repos.OneToManyWriteKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
class KtorOneToManyWriteKeyValueRepo<Key, Value> (
private val baseUrl: String,
private val client: HttpClient = HttpClient(),
private val keySerializer: KSerializer<Key>,
private val valueSerializer: KSerializer<Value>,
) : OneToManyWriteKeyValueRepo<Key, Value> {
private val keyValueSerializer = PairSerializer(keySerializer, valueSerializer)
override suspend fun add(k: Key, v: Value) = client.unipost(
buildStandardUrl(
baseUrl,
addRoute,
),
BodyPair(keyValueSerializer, k to v),
Unit.serializer(),
)
override suspend fun remove(k: Key, v: Value) = client.unipost(
buildStandardUrl(
baseUrl,
removeRoute,
),
BodyPair(keyValueSerializer, k to v),
Unit.serializer(),
)
override suspend fun clear(k: Key) = client.unipost(
buildStandardUrl(
baseUrl,
clearRoute,
),
BodyPair(keySerializer, k),
Unit.serializer(),
)
}

View File

@ -0,0 +1,16 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply from: "$mppProjectWithSerializationPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api internalProject("micro_utils.repos.common")
}
}
}
}

View File

@ -0,0 +1,5 @@
package dev.inmo.micro_utils.repos.ktor.common.crud
const val getByPaginationRouting = "getByPagination"
const val getByIdRouting = "getById"
const val containsRouting = "contains"

View File

@ -0,0 +1,10 @@
package dev.inmo.micro_utils.repos.ktor.common.crud
const val newObjectsFlowRouting = "newObjectsFlow"
const val updatedObjectsFlowRouting = "updatedObjectsFlow"
const val deletedObjectsIdsFlowRouting = "deletedObjectsIdsFlow"
const val createRouting = "create"
const val updateRouting = "update"
const val updateManyRouting = "updateMany"
const val deleteByIdRouting = "deleteById"

View File

@ -0,0 +1,4 @@
package dev.inmo.micro_utils.repos.ktor.common.key_value
const val keyParameterName = "key"
const val reversedParameterName = "reversed"

View File

@ -0,0 +1,9 @@
package dev.inmo.micro_utils.repos.ktor.common.key_value
import kotlinx.serialization.Serializable
@Serializable
data class KeyValuePostObject<K, V> (
val key: K,
val value: V,
)

View File

@ -0,0 +1,12 @@
package dev.inmo.micro_utils.repos.ktor.common.key_value
const val getRoute = "get"
const val valuesRoute = "values"
const val keysRoute = "keys"
const val containsRoute = "contains"
const val countRoute = "count"
const val onNewValueRoute = "onNewValue"
const val onValueRemovedRoute = "onValueRemoved"
const val setRoute = "set"
const val unsetRoute = "unset"

View File

@ -0,0 +1,5 @@
package dev.inmo.micro_utils.repos.ktor.common.one_to_many
const val keyParameterName = "key"
const val valueParameterName = "value"
const val reversedParameterName = "reversed"

View File

@ -0,0 +1,12 @@
package dev.inmo.micro_utils.repos.ktor.common.one_to_many
const val getRoute = "get"
const val keysRoute = "keys"
const val containsByKeyRoute = "containsByKey"
const val containsByKeyValueRoute = "containsByKeyValue"
const val countByKeyRoute = "countByKey"
const val countRoute = "count"
const val addRoute = "add"
const val removeRoute = "remove"
const val clearRoute = "clear"

View File

@ -0,0 +1,23 @@
plugins {
id "org.jetbrains.kotlin.multiplatform"
id "org.jetbrains.kotlin.plugin.serialization"
}
apply from: "$mppJavaProjectPresetPath"
kotlin {
sourceSets {
commonMain {
dependencies {
api internalProject("micro_utils.repos.ktor.common")
}
}
jvmMain {
dependencies {
api "io.ktor:ktor-server:$ktor_version"
api "io.ktor:ktor-server-host-common:$ktor_version"
}
}
}
}

View File

@ -0,0 +1,54 @@
package dev.inmo.micro_utils.repos.ktor.server.crud
import dev.inmo.micro_utils.repos.ktor.common.crud.containsRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.getByIdRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.getByPaginationRouting
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import io.ktor.application.call
import io.ktor.routing.Route
import io.ktor.routing.get
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
fun <ObjectType, IdType> Route.configureReadStandardCrudRepoRoutes(
originalRepo: ReadStandardCRUDRepo<ObjectType, IdType>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
idsSerializer: KSerializer<IdType>
) {
val paginationResultSerializer = PaginationResult.serializer(objectsSerializer)
get(getByPaginationRouting) {
val pagination = call.request.queryParameters.extractPagination
call.unianswer(
paginationResultSerializer,
originalRepo.getByPagination(pagination)
)
}
get(getByIdRouting) {
val id = call.decodeUrlQueryValueOrSendError(
"id",
idsSerializer
) ?: return@get
call.unianswer(
objectsNullableSerializer,
originalRepo.getById(id)
)
}
get(containsRouting) {
val id = call.decodeUrlQueryValueOrSendError(
"id",
idsSerializer
) ?: return@get
call.unianswer(
Boolean.serializer(),
originalRepo.contains(id)
)
}
}

View File

@ -0,0 +1,20 @@
package dev.inmo.micro_utils.repos.ktor.server.crud
import dev.inmo.micro_utils.repos.StandardCRUDRepo
import io.ktor.routing.Route
import io.ktor.routing.route
import kotlinx.serialization.KSerializer
fun <ObjectType, IdType, InputValue> Route.configureStandardCrudRepoRoutes(
baseSubpart: String,
originalRepo: StandardCRUDRepo<ObjectType, IdType, InputValue>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>
) {
route(baseSubpart) {
configureReadStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, idsSerializer)
configureWriteStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer)
}
}

View File

@ -0,0 +1,81 @@
package dev.inmo.micro_utils.repos.ktor.server.crud
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.application.call
import io.ktor.routing.Route
import io.ktor.routing.post
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
fun <ObjectType, IdType, InputValue> Route.configureWriteStandardCrudRepoRoutes(
originalRepo: WriteStandardCRUDRepo<ObjectType, IdType, InputValue>,
objectsSerializer: KSerializer<ObjectType>,
objectsNullableSerializer: KSerializer<ObjectType?>,
inputsSerializer: KSerializer<InputValue>,
idsSerializer: KSerializer<IdType>
) {
val listObjectsSerializer = ListSerializer(objectsSerializer)
val listInputSerializer = ListSerializer(inputsSerializer)
val listIdsSerializer = ListSerializer(idsSerializer)
val inputUpdateSerializer = PairSerializer(
idsSerializer,
inputsSerializer
)
val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer)
includeWebsocketHandling(
newObjectsFlowRouting,
originalRepo.newObjectsFlow,
objectsSerializer
)
includeWebsocketHandling(
updatedObjectsFlowRouting,
originalRepo.updatedObjectsFlow,
objectsSerializer
)
includeWebsocketHandling(
deletedObjectsIdsFlowRouting,
originalRepo.deletedObjectsIdsFlow,
idsSerializer
)
post(createRouting) {
call.unianswer(
listObjectsSerializer,
originalRepo.create(
call.uniload(listInputSerializer)
)
)
}
post(updateRouting) {
val (id, input) = call.uniload(inputUpdateSerializer)
call.unianswer(
objectsNullableSerializer,
originalRepo.update(
id, input
)
)
}
post(updateManyRouting) {
val updates = call.uniload(listInputUpdateSerializer)
call.unianswer(
listObjectsSerializer,
originalRepo.update(
updates
)
)
}
post(deleteByIdRouting) {
val ids = call.uniload(listIdsSerializer)
call.unianswer(
Unit.serializer(),
originalRepo.deleteById(
ids
)
)
}
}

View File

@ -0,0 +1,27 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import io.ktor.routing.*
import kotlinx.serialization.KSerializer
fun <K, V> Route.configureStandartKeyValueRepoRoutes(
baseSubpart: String,
originalRepo: StandardKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>,
) {
route(baseSubpart) {
configureReadStandartKeyValueRepoRoutes(
originalRepo,
keySerializer,
valueSerializer,
valueNullableSerializer,
)
configureWriteStandartKeyValueRepoRoutes(
originalRepo,
keySerializer,
valueSerializer,
)
}
}

View File

@ -0,0 +1,72 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.repos.StandardReadKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.application.*
import io.ktor.routing.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
fun <K, V> Route.configureReadStandartKeyValueRepoRoutes (
originalRepo: StandardReadKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
valueNullableSerializer: KSerializer<V?>,
) {
get(getRoute) {
val key = call.decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
call.unianswer(
valueNullableSerializer,
originalRepo.get(key)
)
}
get(valuesRoute) {
val parination = call.request.queryParameters.extractPagination;
val reversed = call.decodeUrlQueryValueOrSendError(
reversedParameterName,
Boolean.serializer()
) ?: return@get
call.unianswer(
PaginationResult.serializer(valueSerializer),
originalRepo.values(parination, reversed)
)
}
get(keysRoute) {
val parination = call.request.queryParameters.extractPagination;
val reversed = call.decodeUrlQueryValueOrSendError(
reversedParameterName,
Boolean.serializer()
) ?: return@get
call.unianswer(
PaginationResult.serializer(keySerializer),
originalRepo.keys(parination, reversed)
)
}
get(containsRoute) {
val key = call.decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
call.unianswer(
Boolean.serializer(),
originalRepo.contains(key)
)
}
get(countRoute) {
call.unianswer(
Long.serializer(),
originalRepo.count()
)
}
}

View File

@ -0,0 +1,42 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.repos.StandardWriteKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.application.*
import io.ktor.routing.*
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.PairSerializer
fun <K, V> Route.configureWriteStandartKeyValueRepoRoutes (
originalRepo: StandardWriteKeyValueRepo<K, V>,
keySerializer: KSerializer<K>,
valueSerializer: KSerializer<V>,
) {
includeWebsocketHandling(
onNewValueRoute,
originalRepo.onNewValue,
PairSerializer(keySerializer, valueSerializer)
)
includeWebsocketHandling(
onValueRemovedRoute,
originalRepo.onValueRemoved,
keySerializer
)
post(setRoute) {
val (key, value) = call.uniload(
KeyValuePostObject.serializer(keySerializer, valueSerializer)
)
originalRepo.set(key, value)
}
post(unsetRoute) {
val key = call.uniload(
keySerializer
)
originalRepo.unset(key)
}
}

View File

@ -0,0 +1,18 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.repos.OneToManyKeyValueRepo
import io.ktor.routing.Route
import io.ktor.routing.route
import kotlinx.serialization.KSerializer
fun <Key, Value> Route.configureOneToManyKeyValueRepoRoutes(
baseSubpart: String,
originalRepo: OneToManyKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSealizer: KSerializer<Value>,
) {
route(baseSubpart) {
configureOneToManyReadKeyValueRepoRoutes(originalRepo, keySerializer, valueSealizer)
configureOneToManyWriteKeyValueRepoRoutes(originalRepo, keySerializer, valueSealizer)
}
}

View File

@ -0,0 +1,96 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.repos.OneToManyReadKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.application.call
import io.ktor.routing.Route
import io.ktor.routing.get
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer
fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
originalRepo: OneToManyReadKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSealizer: KSerializer<Value>,
) {
val paginationKeyResult = PaginationResult.serializer(keySerializer)
val paginationValueResult = PaginationResult.serializer(valueSealizer)
get(getRoute) {
val pagination = call.request.queryParameters.extractPagination
val key = call.decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
val reversed = call.decodeUrlQueryValue(
reversedParameterName,
Boolean.serializer()
) ?: false
call.unianswer(
paginationValueResult,
originalRepo.get(key, pagination, reversed)
)
}
get(keysRoute) {
val pagination = call.request.queryParameters.extractPagination
val reversed = call.decodeUrlQueryValue(
reversedParameterName,
Boolean.serializer()
) ?: false
call.unianswer(
paginationKeyResult,
originalRepo.keys(pagination, reversed)
)
}
get(containsByKeyRoute) {
val key = call.decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
call.unianswer(
Boolean.serializer(),
originalRepo.contains(key)
)
}
get(containsByKeyValueRoute) {
val key = call.decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
val value = call.decodeUrlQueryValueOrSendError(
valueParameterName,
valueSealizer
) ?: return@get
call.unianswer(
Boolean.serializer(),
originalRepo.contains(key, value)
)
}
get(countByKeyRoute) {
val key = call.decodeUrlQueryValueOrSendError(
keyParameterName,
keySerializer
) ?: return@get
call.unianswer(
Long.serializer(),
originalRepo.count(key)
)
}
get(countRoute) {
call.unianswer(
Long.serializer(),
originalRepo.count()
)
}
}

View File

@ -0,0 +1,49 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.repos.OneToManyWriteKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.application.call
import io.ktor.routing.Route
import io.ktor.routing.post
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.serializer
fun <Key, Value> Route.configureOneToManyWriteKeyValueRepoRoutes(
originalRepo: OneToManyWriteKeyValueRepo<Key, Value>,
keySerializer: KSerializer<Key>,
valueSealizer: KSerializer<Value>,
) {
val keyValueSerializer = PairSerializer(keySerializer, valueSealizer)
post(addRoute) {
val obj = call.uniload(
keyValueSerializer
)
call.unianswer(
Unit.serializer(),
originalRepo.add(obj.first, obj.second)
)
}
post(removeRoute) {
val obj = call.uniload(
keyValueSerializer
)
call.unianswer(
Unit.serializer(),
originalRepo.remove(obj.first, obj.second),
)
}
post(clearRoute) {
val key = call.uniload(keySerializer)
call.unianswer(
Unit.serializer(),
originalRepo.clear(key),
)
}
}

View File

@ -6,6 +6,10 @@ String[] includes = [
":pagination:ktor:common",
":pagination:ktor:server",
":repos:common",
":repos:exposed",
":repos:ktor:client",
":repos:ktor:common",
":repos:ktor:server",
]