complete improvements in repos ktor parts

This commit is contained in:
InsanusMokrassar 2022-06-04 00:22:36 +06:00
parent eaa143f7d7
commit 78903cd4eb
30 changed files with 805 additions and 38 deletions

View File

@ -2,6 +2,12 @@
## 0.11.0 ## 0.11.0
* `Ktor`
*
* `Repos`
* `Ktor`:
* Fully rewritten work with all declared repositories
## 0.10.8 ## 0.10.8
* `Common` * `Common`

View File

@ -0,0 +1,15 @@
package dev.inmo.micro_utils.ktor.client
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.statement.HttpResponse
import io.ktor.http.isSuccess
inline fun HttpResponse.throwOnUnsuccess(
unsuccessMessage: () -> String
) {
if (status.isSuccess()) {
return
}
throw ClientRequestException(this, unsuccessMessage())
}

View File

@ -0,0 +1,15 @@
package dev.inmo.micro_utils.ktor.server
import io.ktor.server.application.ApplicationCall
import io.ktor.server.response.responseType
import io.ktor.util.InternalAPI
import io.ktor.util.reflect.TypeInfo
@InternalAPI
suspend fun <T : Any> ApplicationCall.respond(
message: T,
typeInfo: TypeInfo
) {
response.responseType = typeInfo
response.pipeline.execute(this, message as Any)
}

View File

@ -104,6 +104,13 @@ interface OneToManyKeyValueRepo<Key, Value> : ReadOneToManyKeyValueRepo<Key, Val
} }
typealias KeyValuesRepo<Key,Value> = OneToManyKeyValueRepo<Key, Value> typealias KeyValuesRepo<Key,Value> = OneToManyKeyValueRepo<Key, Value>
class DelegateBasedOneToManyKeyValueRepo<Key, Value>(
readDelegate: ReadOneToManyKeyValueRepo<Key, Value>,
writeDelegate: WriteOneToManyKeyValueRepo<Key, Value>
) : OneToManyKeyValueRepo<Key, Value>,
ReadOneToManyKeyValueRepo<Key, Value> by readDelegate,
WriteOneToManyKeyValueRepo<Key, Value> by writeDelegate
suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove( suspend inline fun <Key, Value> WriteOneToManyKeyValueRepo<Key, Value>.remove(
keysAndValues: List<Pair<Key, List<Value>>> keysAndValues: List<Pair<Key, List<Value>>>
) = remove(keysAndValues.toMap()) ) = remove(keysAndValues.toMap())

View File

@ -80,6 +80,10 @@ class MapWriteOneToManyKeyValueRepo<Key, Value>(
_onValueRemoved.emit(k to v) _onValueRemoved.emit(k to v)
} }
} }
if (map[k] ?.isEmpty() == true) {
map.remove(k)
_onDataCleared.emit(k)
}
} }
} }

View File

@ -4,6 +4,7 @@ import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.* import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.countRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.idParameterName import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import io.ktor.client.HttpClient import io.ktor.client.HttpClient

View File

@ -3,6 +3,7 @@ package dev.inmo.micro_utils.repos.ktor.client.crud
import dev.inmo.micro_utils.ktor.common.* import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.countRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.idParameterName import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import io.ktor.client.HttpClient import io.ktor.client.HttpClient

View File

@ -11,11 +11,7 @@ import io.ktor.client.request.*
import io.ktor.client.statement.HttpResponse import io.ktor.client.statement.HttpResponse
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.http.contentType import io.ktor.http.contentType
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.*
class KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue> ( class KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue> (
private val baseUrl: String, private val baseUrl: String,
@ -50,7 +46,7 @@ class KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue> (
buildStandardUrl(baseUrl, deleteByIdRouting) buildStandardUrl(baseUrl, deleteByIdRouting)
) { ) {
deleteByIdSetup(ids) deleteByIdSetup(ids)
}.status }.throwOnUnsuccess { "Unable to delete $ids" }
} }
companion object { companion object {

View File

@ -4,8 +4,12 @@ import dev.inmo.micro_utils.ktor.client.*
import dev.inmo.micro_utils.ktor.common.* import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.containsRoute
import dev.inmo.micro_utils.repos.ktor.common.countRoute
import dev.inmo.micro_utils.repos.ktor.common.key_value.* import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName import dev.inmo.micro_utils.repos.ktor.common.key_value.keyParameterName
import dev.inmo.micro_utils.repos.ktor.common.key_value.reversedParameterName
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer

View File

@ -4,9 +4,8 @@ import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.* import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.containsRoute
import dev.inmo.micro_utils.repos.ktor.common.key_value.* import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.containsByKeyRoute
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.body import io.ktor.client.call.body
@ -83,7 +82,7 @@ class KtorReadStandardKeyValueRepoClient<Key, Value>(
override suspend fun count(): Long = httpClient.get( override suspend fun count(): Long = httpClient.get(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
countRouting dev.inmo.micro_utils.repos.ktor.common.countRoute
) )
) { ) {
contentType(contentType) contentType(contentType)

View File

@ -1,21 +1,17 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
import dev.inmo.micro_utils.ktor.client.throwOnUnsuccess
import dev.inmo.micro_utils.ktor.common.* import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.key_value.* import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.request.post import io.ktor.client.request.post
import io.ktor.http.* import io.ktor.http.*
import io.ktor.util.InternalAPI import io.ktor.util.InternalAPI
import io.ktor.util.reflect.TypeInfo import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo import io.ktor.util.reflect.typeInfo
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.serialization.*
class KtorWriteStandardKeyValueRepoClient<Key, Value>( class KtorWriteStandardKeyValueRepoClient<Key, Value>(
private val baseUrl: String, private val baseUrl: String,
@ -35,7 +31,7 @@ class KtorWriteStandardKeyValueRepoClient<Key, Value>(
body = toUnset body = toUnset
bodyType = objectsListTypeInfo bodyType = objectsListTypeInfo
contentType(contentType) contentType(contentType)
}.status }.throwOnUnsuccess { "Unable to unset data with values $toUnset" }
} }
@OptIn(InternalAPI::class) @OptIn(InternalAPI::class)
@ -46,7 +42,7 @@ class KtorWriteStandardKeyValueRepoClient<Key, Value>(
body = toUnset body = toUnset
bodyType = idsListTypeInfo bodyType = idsListTypeInfo
contentType(contentType) contentType(contentType)
}.status }.throwOnUnsuccess { "Unable to unset $toUnset" }
} }
@OptIn(InternalAPI::class) @OptIn(InternalAPI::class)
@ -57,7 +53,7 @@ class KtorWriteStandardKeyValueRepoClient<Key, Value>(
body = toSet body = toSet
bodyType = idsToObjectsMapTypeInfo bodyType = idsToObjectsMapTypeInfo
contentType(contentType) contentType(contentType)
}.status }.throwOnUnsuccess { "Unable to set $toSet" }
} }
companion object { companion object {

View File

@ -0,0 +1,159 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.http.*
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.serialization.*
class KtorReadStandardKeyValuesRepoClient<Key, Value>(
private val baseUrl: String,
private val httpClient: HttpClient,
private val contentType: ContentType,
private val paginationResultValuesTypeInfo: TypeInfo,
private val paginationResultKeysTypeInfo: TypeInfo,
private val keySerializer: suspend (Key) -> String,
private val valueSerializer: suspend (Value) -> String
) : ReadOneToManyKeyValueRepo<Key, Value> {
override suspend fun get(
k: Key,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Value> = httpClient.get(
buildStandardUrl(
baseUrl,
getRoute,
pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()) + (keyParameterName to keySerializer(k))
)
) {
contentType(contentType)
}.body(paginationResultValuesTypeInfo)
override suspend fun keys(
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = httpClient.get(
buildStandardUrl(
baseUrl,
keysRoute,
pagination.asUrlQueryParts + (reversedParameterName to reversed.toString())
)
) {
contentType(contentType)
}.body(paginationResultKeysTypeInfo)
override suspend fun keys(
v: Value,
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = httpClient.get(
buildStandardUrl(
baseUrl,
keysRoute,
pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()) + (valueParameterName to valueSerializer(v))
)
) {
contentType(contentType)
}.body(paginationResultKeysTypeInfo)
override suspend fun contains(k: Key): Boolean = httpClient.get(
buildStandardUrl(
baseUrl,
containsRoute,
keyParameterName to keySerializer(k)
)
) {
contentType(contentType)
}.body()
override suspend fun contains(k: Key, v: Value): Boolean = httpClient.get(
buildStandardUrl(
baseUrl,
containsRoute,
keyParameterName to keySerializer(k),
valueParameterName to valueSerializer(v)
)
) {
contentType(contentType)
}.body()
override suspend fun count(): Long = httpClient.get(
buildStandardUrl(
baseUrl,
countRouting
)
) {
contentType(contentType)
}.body()
override suspend fun count(k: Key): Long = httpClient.get(
buildStandardUrl(
baseUrl,
countRouting,
keyParameterName to keySerializer(k),
)
) {
contentType(contentType)
}.body()
}
inline fun <reified Key, reified Value> KtorReadStandardKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline keySerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorReadStandardKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
typeInfo<PaginationResult<Value>>(),
typeInfo<PaginationResult<Key>>(),
keySerializer,
valueSerializer
)
inline fun <reified Key, reified Value> KtorReadStandardKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StringFormat,
contentType: ContentType,
) = KtorReadStandardKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeToString(idsSerializer, it).encodeURLQueryComponent()
}
) {
serialFormat.encodeToString(valueSerializer, it).encodeURLQueryComponent()
}
inline fun <reified Key, reified Value> KtorReadStandardKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<Key>,
valuesSerializer: KSerializer<Value>,
serialFormat: BinaryFormat,
contentType: ContentType,
) = KtorReadStandardKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeHex(idsSerializer, it)
}
) {
serialFormat.encodeHex(valuesSerializer, it)
}

View File

@ -0,0 +1,89 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient
import io.ktor.http.ContentType
import io.ktor.http.encodeURLQueryComponent
import kotlinx.serialization.*
class KtorStandardKeyValuesRepoClient<Key, Value> (
readDelegate: ReadOneToManyKeyValueRepo<Key, Value>,
writeDelegate: WriteOneToManyKeyValueRepo<Key, Value>
) : OneToManyKeyValueRepo<Key, Value> by DelegateBasedOneToManyKeyValueRepo(
readDelegate,
writeDelegate
) {
companion object {
inline operator fun <reified Key : Any, reified Value : Any> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline keySerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorStandardKeyValuesRepoClient(
KtorReadStandardKeyValuesRepoClient(
baseUrl,
httpClient,
contentType,
keySerializer,
valueSerializer
),
KtorWriteStandardKeyValuesRepoClient(
baseUrl,
httpClient,
contentType
)
)
inline operator fun <reified Key : Any, reified Value : Any> invoke(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
contentType: ContentType,
noinline keySerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorStandardKeyValuesRepoClient(
buildStandardUrl(baseUrl, subpart),
httpClient,
contentType,
keySerializer,
valueSerializer
)
}
}
inline fun <reified Key : Any, reified Value : Any> KtorStandardKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
keySerializer: SerializationStrategy<Key>,
valueSerializer: SerializationStrategy<Value>,
serialFormat: StringFormat,
) = KtorStandardKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeToString(keySerializer, it).encodeURLQueryComponent()
}
) {
serialFormat.encodeToString(valueSerializer, it).encodeURLQueryComponent()
}
inline fun <reified Key : Any, reified Value : Any> KtorStandardKeyValuesRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
keySerializer: SerializationStrategy<Key>,
valueSerializer: SerializationStrategy<Value>,
serialFormat: BinaryFormat,
) = KtorStandardKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeHex(keySerializer, it)
}
) {
serialFormat.encodeHex(valueSerializer, it)
}

View File

@ -0,0 +1,106 @@
package dev.inmo.micro_utils.repos.ktor.client.one_to_many
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
import dev.inmo.micro_utils.ktor.client.throwOnUnsuccess
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.client.HttpClient
import io.ktor.client.request.post
import io.ktor.http.*
import io.ktor.util.InternalAPI
import io.ktor.util.reflect.TypeInfo
import io.ktor.util.reflect.typeInfo
import kotlinx.coroutines.flow.Flow
class KtorWriteStandardKeyValuesRepoClient<Key : Any, Value : Any>(
private val baseUrl: String,
private val httpClient: HttpClient,
private val contentType: ContentType,
override val onNewValue: Flow<Pair<Key, Value>>,
override val onValueRemoved: Flow<Pair<Key, Value>>,
override val onDataCleared: Flow<Key>,
private val keyTypeInfo: TypeInfo,
private val valueTypeInfo: TypeInfo,
private val keyToValuesMapTypeInfo: TypeInfo
) : WriteOneToManyKeyValueRepo<Key, Value> {
@OptIn(InternalAPI::class)
override suspend fun add(toAdd: Map<Key, List<Value>>) {
httpClient.post(
buildStandardUrl(baseUrl, addRoute)
) {
body = toAdd
bodyType = keyToValuesMapTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to add $toAdd" }
}
@OptIn(InternalAPI::class)
override suspend fun remove(toRemove: Map<Key, List<Value>>) {
httpClient.post(
buildStandardUrl(baseUrl, removeRoute)
) {
body = toRemove
bodyType = keyToValuesMapTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to remove $toRemove" }
}
@OptIn(InternalAPI::class)
override suspend fun clear(k: Key) {
httpClient.post(
buildStandardUrl(baseUrl, clearRoute)
) {
body = k
bodyType = keyTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to clear data with key $k" }
}
@OptIn(InternalAPI::class)
override suspend fun clearWithValue(v: Value) {
httpClient.post(
buildStandardUrl(baseUrl, clearWithValueRoute)
) {
body = v
bodyType = valueTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to clear data with value $v" }
}
@OptIn(InternalAPI::class)
override suspend fun set(toSet: Map<Key, List<Value>>) {
httpClient.post(
buildStandardUrl(baseUrl, setRoute)
) {
body = toSet
bodyType = keyToValuesMapTypeInfo
contentType(contentType)
}.throwOnUnsuccess { "Unable to set data $toSet" }
}
companion object {
inline operator fun <reified Key : Any, reified Value : Any> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType
) = KtorWriteStandardKeyValuesRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onValueRemovedRoute),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onDataClearedRoute),
),
typeInfo<Key>(),
typeInfo<Value>(),
typeInfo<Map<Key, List<Value>>>()
)
}
}

View File

@ -0,0 +1,3 @@
package dev.inmo.micro_utils.repos.ktor.common
const val containsRoute = "contains"

View File

@ -0,0 +1,3 @@
package dev.inmo.micro_utils.repos.ktor.common
const val countRoute = "count"

View File

@ -0,0 +1,3 @@
package dev.inmo.micro_utils.repos.ktor.common
const val countRouting = "count"

View File

@ -3,4 +3,3 @@ package dev.inmo.micro_utils.repos.ktor.common.crud
const val getByPaginationRouting = "getByPagination" const val getByPaginationRouting = "getByPagination"
const val getByIdRouting = "getById" const val getByIdRouting = "getById"
const val containsRouting = "contains" const val containsRouting = "contains"
const val countRouting = "count"

View File

@ -3,8 +3,8 @@ package dev.inmo.micro_utils.repos.ktor.common.key_value
const val getRoute = "get" const val getRoute = "get"
const val valuesRoute = "values" const val valuesRoute = "values"
const val keysRoute = "keys" const val keysRoute = "keys"
const val containsRoute = "contains" const val containsRoute = dev.inmo.micro_utils.repos.ktor.common.containsRoute
const val countRoute = "count" const val countRoute = dev.inmo.micro_utils.repos.ktor.common.countRoute
const val onNewValueRoute = "onNewValue" const val onNewValueRoute = "onNewValue"
const val onValueRemovedRoute = "onValueRemoved" const val onValueRemovedRoute = "onValueRemoved"

View File

@ -5,7 +5,7 @@ const val keysRoute = "keys"
const val containsByKeyRoute = "containsByKey" const val containsByKeyRoute = "containsByKey"
const val containsByKeyValueRoute = "containsByKeyValue" const val containsByKeyValueRoute = "containsByKeyValue"
const val countByKeyRoute = "countByKey" const val countByKeyRoute = "countByKey"
const val countRoute = "count" const val countRoute = dev.inmo.micro_utils.repos.ktor.common.countRoute
const val onNewValueRoute = "onNewValue" const val onNewValueRoute = "onNewValue"
const val onValueRemovedRoute = "onValueRemoved" const val onValueRemovedRoute = "onValueRemoved"

View File

@ -21,7 +21,7 @@ class CRUDTests {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun testCRUDFunctions() { fun testCRUDFunctions() {
runTest() { runTest {
val map = mutableMapOf<Int, ComplexData>() val map = mutableMapOf<Int, ComplexData>()
val repo = MapCRUDRepo<ComplexData, Int, SimpleData>( val repo = MapCRUDRepo<ComplexData, Int, SimpleData>(
map, map,

View File

@ -22,8 +22,8 @@ import kotlin.test.*
class KVTests { class KVTests {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
fun testCRUDFunctions() { fun testKVFunctions() {
runTest() { runTest {
val map = mutableMapOf<Int, ComplexData>() val map = mutableMapOf<Int, ComplexData>()
val repo = MapKeyValueRepo<Int, ComplexData>(map) val repo = MapKeyValueRepo<Int, ComplexData>(map)
val server = io.ktor.server.engine.embeddedServer( val server = io.ktor.server.engine.embeddedServer(

View File

@ -0,0 +1,141 @@
import dev.inmo.micro_utils.pagination.firstPageWithOneElementPagination
import dev.inmo.micro_utils.pagination.utils.getAllWithNextPaging
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.ktor.client.key_value.KtorStandardKeyValueRepoClient
import dev.inmo.micro_utils.repos.ktor.client.one_to_many.KtorStandardKeyValuesRepoClient
import dev.inmo.micro_utils.repos.ktor.server.one_to_many.configureStandardKeyValuesRepoRoutes
import io.ktor.client.HttpClient
import io.ktor.client.plugins.logging.Logging
import io.ktor.http.ContentType
import io.ktor.serialization.kotlinx.KotlinxWebsocketSerializationConverter
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.install
import io.ktor.server.cio.CIO
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.routing.routing
import io.ktor.server.websocket.WebSockets
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.json.Json
import kotlin.test.*
class KVsTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testKVsFunctions() {
runTest {
val map = mutableMapOf<Int, MutableList<ComplexData>>()
val repo = MapOneToManyKeyValueRepo(map)
val server = io.ktor.server.engine.embeddedServer(
CIO,
23456,
"127.0.0.1"
) {
install(ContentNegotiation) {
json()
}
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
routing {
configureStandardKeyValuesRepoRoutes(
repo,
Int.serializer(),
ComplexData.serializer(),
Json {}
)
}
}.start(false)
val client = HttpClient {
install(io.ktor.client.plugins.contentnegotiation.ContentNegotiation) {
json()
}
install(Logging)
install(io.ktor.client.plugins.websocket.WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
}
val crudClient = KtorStandardKeyValuesRepoClient(
"http://127.0.0.1:23456",
client,
ContentType.Application.Json,
Int.serializer(),
ComplexData.serializer(),
Json
)
val dataInOneKey = ComplexData(1, title = "Example1")
val dataInMultipleKeys = ComplexData(2, title = "Example2")
val repeatCount = 3
val dataList = listOf(
1 to listOf(dataInOneKey)
) + (0 until repeatCount).map {
(it + 2) to listOf(dataInMultipleKeys)
}
dataList.forEachIndexed { i, (id, data) ->
crudClient.set(id, data)
assertEquals(i + 1, map.size)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(i + 1L, crudClient.count())
dataList.take(i + 1).forEach { (id, data) ->
assertContentEquals(data, map[id])
assertContentEquals(data, crudClient.getAll(id))
assertContentEquals(map[id], crudClient.getAll(id))
}
}
dataList.forEach { (key, data) ->
assertTrue(crudClient.contains(key))
assertContentEquals(data, crudClient.getAll(key))
}
assertEquals(
dataList.mapNotNull { if (it.second.contains(dataInMultipleKeys)) it.first else null },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(dataInMultipleKeys, it)
}
)
assertEquals(
dataList.mapNotNull { if (it.second.contains(dataInOneKey)) it.first else null },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(dataInOneKey, it)
}
)
assertEquals(
dataList.map { it.first },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(it)
}
)
assertEquals(
dataList.map { it.first },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(it)
}
)
assertEquals(dataList.size.toLong(), crudClient.count())
crudClient.remove(dataList.filter { it.second.contains(dataInMultipleKeys) })
println(map)
assertEquals(
dataList.filter { it.second.contains(dataInOneKey) }.size.toLong(),
crudClient.count()
)
crudClient.remove(dataList.filter { it.second.contains(dataInOneKey) })
assertEquals(
0,
crudClient.count()
)
server.stop()
}
}
}

View File

@ -6,6 +6,7 @@ import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.countRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.server.application.call import io.ktor.server.application.call

View File

@ -4,6 +4,7 @@ import dev.inmo.micro_utils.ktor.common.decodeHex
import dev.inmo.micro_utils.ktor.server.* import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.extractPagination import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ktor.common.countRouting
import dev.inmo.micro_utils.repos.ktor.common.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.idParameterName import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import io.ktor.http.ContentType import io.ktor.http.ContentType

View File

@ -6,8 +6,12 @@ import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.containsRoute
import dev.inmo.micro_utils.repos.ktor.common.countRoute
import dev.inmo.micro_utils.repos.ktor.common.key_value.* import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName import dev.inmo.micro_utils.repos.ktor.common.key_value.keyParameterName
import dev.inmo.micro_utils.repos.ktor.common.key_value.reversedParameterName
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.server.application.call import io.ktor.server.application.call
import io.ktor.server.routing.Route import io.ktor.server.routing.Route

View File

@ -4,21 +4,20 @@ import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.ktor.server.* import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.PaginationResult import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.idParameterName import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.containsRoute
import dev.inmo.micro_utils.repos.ktor.common.countRoute
import dev.inmo.micro_utils.repos.ktor.common.key_value.* import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName import dev.inmo.micro_utils.repos.ktor.common.reversedParameterName
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.call import io.ktor.server.application.call
import io.ktor.server.response.respond import io.ktor.server.response.respond
import io.ktor.server.response.responseType
import io.ktor.server.routing.Route import io.ktor.server.routing.Route
import io.ktor.server.routing.get import io.ktor.server.routing.get
import io.ktor.util.InternalAPI import io.ktor.util.InternalAPI
import io.ktor.util.reflect.typeInfo import io.ktor.util.reflect.typeInfo
import kotlinx.serialization.* import kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
@OptIn(InternalAPI::class) @OptIn(InternalAPI::class)
inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoRoutes ( inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoRoutes (
@ -26,6 +25,9 @@ inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoR
noinline idDeserializer: suspend (String) -> Key, noinline idDeserializer: suspend (String) -> Key,
noinline valueDeserializer: suspend (String) -> Value noinline valueDeserializer: suspend (String) -> Value
) { ) {
val paginationWithValuesTypeInfo = typeInfo<PaginationResult<Value>>()
val paginationWithKeysTypeInfo = typeInfo<PaginationResult<Key>>()
get(getRoute) { get(getRoute) {
val key = idDeserializer( val key = idDeserializer(
call.getQueryParameterOrSendError(idParameterName) ?: return@get call.getQueryParameterOrSendError(idParameterName) ?: return@get
@ -36,13 +38,14 @@ inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoR
} ?: call.respond(HttpStatusCode.NoContent) } ?: call.respond(HttpStatusCode.NoContent)
} }
val paginationWithValuesTypeInfo = typeInfo<PaginationResult<Value>>()
get(valuesRoute) { get(valuesRoute) {
val pagination = call.request.queryParameters.extractPagination val pagination = call.request.queryParameters.extractPagination
val reversed = call.getQueryParameter(reversedParameterName) ?.toBoolean() ?: false val reversed = call.getQueryParameter(reversedParameterName) ?.toBoolean() ?: false
call.response.responseType = paginationWithValuesTypeInfo call.respond(
call.response.pipeline.execute(call, originalRepo.values(pagination, reversed) as Any) originalRepo.values(pagination, reversed),
paginationWithValuesTypeInfo
)
} }
get(keysRoute) { get(keysRoute) {
@ -53,7 +56,8 @@ inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoR
} }
call.respond( call.respond(
value ?.let { originalRepo.keys(value, pagination, reversed) } ?: originalRepo.keys(pagination, reversed) value ?.let { originalRepo.keys(value, pagination, reversed) } ?: originalRepo.keys(pagination, reversed),
paginationWithKeysTypeInfo
) )
} }
@ -62,9 +66,7 @@ inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoR
call.getQueryParameterOrSendError(idParameterName) ?: return@get call.getQueryParameterOrSendError(idParameterName) ?: return@get
) )
call.respond( call.respond(originalRepo.contains(key))
originalRepo.contains(key)
)
} }
get(countRoute) { get(countRoute) {

View File

@ -0,0 +1,47 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.repos.*
import io.ktor.http.*
import io.ktor.server.routing.Route
import kotlinx.serialization.*
inline fun <reified Key : Any, reified Value : Any> Route.configureStandardKeyValuesRepoRoutes (
originalRepo: OneToManyKeyValueRepo<Key, Value>,
noinline idDeserializer: suspend (String) -> Key,
noinline valueDeserializer: suspend (String) -> Value
) {
configureReadStandardKeyValuesRepoRoutes(originalRepo, idDeserializer, valueDeserializer)
configureWriteStandardKeyValuesRepoRoutes(originalRepo)
}
inline fun <reified Key : Any, reified Value : Any> Route.configureStandardKeyValuesRepoRoutes(
originalRepo: OneToManyKeyValueRepo<Key, Value>,
idsSerializer: DeserializationStrategy<Key>,
valueSerializer: DeserializationStrategy<Value>,
serialFormat: StringFormat
) = configureStandardKeyValuesRepoRoutes(
originalRepo,
{
serialFormat.decodeFromString(idsSerializer, it.decodeURLQueryComponent())
},
{
serialFormat.decodeFromString(valueSerializer, it.decodeURLQueryComponent())
}
)
inline fun <reified Key : Any, reified Value : Any> Route.configureStandardKeyValuesRepoRoutes(
originalRepo: OneToManyKeyValueRepo<Key, Value>,
idsSerializer: DeserializationStrategy<Key>,
valueSerializer: DeserializationStrategy<Value>,
serialFormat: BinaryFormat
) = configureStandardKeyValuesRepoRoutes(
originalRepo,
{
serialFormat.decodeHex(idsSerializer, it)
},
{
serialFormat.decodeHex(valueSerializer, it)
}
)

View File

@ -0,0 +1,107 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.pagination.PaginationResult
import dev.inmo.micro_utils.pagination.extractPagination
import dev.inmo.micro_utils.repos.ReadOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.containsRoute
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.http.*
import io.ktor.server.application.call
import io.ktor.server.response.respond
import io.ktor.server.routing.Route
import io.ktor.server.routing.get
import io.ktor.util.InternalAPI
import io.ktor.util.reflect.typeInfo
import kotlinx.serialization.*
@OptIn(InternalAPI::class)
inline fun <reified Key, reified Value> Route.configureReadStandardKeyValuesRepoRoutes (
originalRepo: ReadOneToManyKeyValueRepo<Key, Value>,
noinline idDeserializer: suspend (String) -> Key,
noinline valueDeserializer: suspend (String) -> Value
) {
val paginationWithValuesTypeInfo = typeInfo<PaginationResult<Value>>()
val paginationWithKeysTypeInfo = typeInfo<PaginationResult<Key>>()
get(getRoute) {
val key = idDeserializer(
call.getQueryParameterOrSendError(keyParameterName) ?: return@get
)
val pagination = call.request.queryParameters.extractPagination
val reversed = call.getQueryParameter(reversedParameterName) ?.toBoolean() ?: false
call.respond(
originalRepo.get(key, pagination, reversed),
paginationWithValuesTypeInfo
)
}
get(keysRoute) {
val pagination = call.request.queryParameters.extractPagination
val reversed = call.getQueryParameterOrSendError(reversedParameterName) ?.toBoolean() ?: false
val value = call.getQueryParameter(valueParameterName) ?.let {
valueDeserializer(it)
}
call.respond(
value ?.let { originalRepo.keys(value, pagination, reversed) } ?: originalRepo.keys(pagination, reversed),
paginationWithKeysTypeInfo
)
}
get(containsRoute) {
val key = idDeserializer(
call.getQueryParameterOrSendError(keyParameterName) ?: return@get
)
val value = call.getQueryParameter(valueParameterName) ?.let {
valueDeserializer(it)
}
call.respond(
value ?.let { originalRepo.contains(key, value) } ?: originalRepo.contains(key)
)
}
get(dev.inmo.micro_utils.repos.ktor.common.countRoute) {
val id = call.getQueryParameter(keyParameterName) ?.let {
idDeserializer(it)
}
call.respond(
id ?.let { originalRepo.count(it) } ?: originalRepo.count()
)
}
}
inline fun <reified Key, reified Value> Route.configureReadStandardKeyValuesRepoRoutes(
originalRepo: ReadOneToManyKeyValueRepo<Key, Value>,
idsSerializer: DeserializationStrategy<Key>,
valueSerializer: DeserializationStrategy<Value>,
serialFormat: StringFormat
) = configureReadStandardKeyValuesRepoRoutes(
originalRepo,
{
serialFormat.decodeFromString(idsSerializer, it.decodeURLQueryComponent())
},
{
serialFormat.decodeFromString(valueSerializer, it.decodeURLQueryComponent())
}
)
inline fun <reified Key, reified Value> Route.configureReadStandardKeyValuesRepoRoutes(
originalRepo: ReadOneToManyKeyValueRepo<Key, Value>,
idsSerializer: DeserializationStrategy<Key>,
valueSerializer: DeserializationStrategy<Value>,
serialFormat: BinaryFormat
) = configureReadStandardKeyValuesRepoRoutes(
originalRepo,
{
serialFormat.decodeHex(idsSerializer, it)
},
{
serialFormat.decodeHex(valueSerializer, it)
}
)

View File

@ -0,0 +1,58 @@
package dev.inmo.micro_utils.repos.ktor.server.one_to_many
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.repos.WriteOneToManyKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.one_to_many.*
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.call
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.routing.Route
import io.ktor.server.routing.post
import io.ktor.util.reflect.typeInfo
inline fun <reified Key : Any, reified Value : Any> Route.configureWriteStandardKeyValuesRepoRoutes (
originalRepo: WriteOneToManyKeyValueRepo<Key, Value>
) {
includeWebsocketHandling(
onNewValueRoute,
originalRepo.onNewValue
)
includeWebsocketHandling(
onValueRemovedRoute,
originalRepo.onValueRemoved
)
includeWebsocketHandling(
onDataClearedRoute,
originalRepo.onDataCleared
)
val mapType = typeInfo<Map<Key, List<Value>>>()
post(addRoute) {
originalRepo.add(call.receive(mapType))
call.respond(HttpStatusCode.OK)
}
post(setRoute) {
originalRepo.set(call.receive(mapType))
call.respond(HttpStatusCode.OK)
}
post(removeRoute) {
originalRepo.remove(call.receive(mapType))
call.respond(HttpStatusCode.OK)
}
post(clearRoute) {
originalRepo.clear(call.receive())
call.respond(HttpStatusCode.OK)
}
post(clearWithValueRoute) {
originalRepo.clearWithValue(call.receive())
call.respond(HttpStatusCode.OK)
}
}