Compare commits

..

2 Commits

24 changed files with 986 additions and 102 deletions

View File

@ -37,17 +37,24 @@ kt-serialization-cbor = { module = "org.jetbrains.kotlinx:kotlinx-serialization-
kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kt-coroutines" } kt-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kt-coroutines" }
kt-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kt-coroutines" } kt-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kt-coroutines" }
kt-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kt-coroutines" }
ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" } ktor-io = { module = "io.ktor:ktor-io", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktor-client = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
ktor-client-java = { module = "io.ktor:ktor-client-java", version.ref = "ktor" } ktor-client-java = { module = "io.ktor:ktor-client-java", version.ref = "ktor" }
ktor-client-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-server = { module = "io.ktor:ktor-server", version.ref = "ktor" } ktor-server = { module = "io.ktor:ktor-server", version.ref = "ktor" }
ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" } ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor" }
ktor-server-host-common = { module = "io.ktor:ktor-server-host-common", version.ref = "ktor" } ktor-server-host-common = { module = "io.ktor:ktor-server-host-common", version.ref = "ktor" }
ktor-websockets = { module = "io.ktor:ktor-websockets", version.ref = "ktor" } ktor-websockets = { module = "io.ktor:ktor-websockets", version.ref = "ktor" }
ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.ref = "ktor" } ktor-server-websockets = { module = "io.ktor:ktor-server-websockets", version.ref = "ktor" }
ktor-server-statusPages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" } ktor-server-statusPages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" }
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" } klock = { module = "com.soywiz.korlibs.klock:klock", version.ref = "klock" }

View File

@ -9,6 +9,7 @@ import io.ktor.client.plugins.websocket.*
import io.ktor.client.request.HttpRequestBuilder import io.ktor.client.request.HttpRequestBuilder
import io.ktor.websocket.Frame import io.ktor.websocket.Frame
import io.ktor.websocket.readBytes import io.ktor.websocket.readBytes
import io.ktor.websocket.serialization.sendSerializedBase
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
@ -18,9 +19,9 @@ import kotlinx.serialization.DeserializationStrategy
* @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish * @param checkReconnection This lambda will be called when it is required to reconnect to websocket to establish
* connection. Must return true in case if must be reconnected. By default always reconnecting * connection. Must return true in case if must be reconnected. By default always reconnecting
*/ */
inline fun <T> HttpClient.createStandardWebsocketFlow( inline fun <reified T> HttpClient.createStandardWebsocketFlow(
url: String, url: String,
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true }, noinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {} noinline requestBuilder: HttpRequestBuilder.() -> Unit = {}
): Flow<T> { ): Flow<T> {
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow") pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")

View File

@ -42,4 +42,11 @@ suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectTyp
interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>, interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
typealias CRUDRepo<ObjectType, IdType, InputValueType> = StandardCRUDRepo<ObjectType, IdType, InputValueType> typealias CRUDRepo<ObjectType, IdType, InputValueType> = StandardCRUDRepo<ObjectType, IdType, InputValueType>
class DelegateBasedStandardCRUDRepo<ObjectType, IdType, InputValueType>(
readDelegate: ReadStandardCRUDRepo<ObjectType, IdType>,
writeDelegate: WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
) : StandardCRUDRepo<ObjectType, IdType, InputValueType>,
ReadStandardCRUDRepo<ObjectType, IdType> by readDelegate,
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> by writeDelegate

View File

@ -50,3 +50,10 @@ interface StandardKeyValueRepo<Key, Value> : ReadStandardKeyValueRepo<Key, Value
} }
} }
typealias KeyValueRepo<Key,Value> = StandardKeyValueRepo<Key, Value> typealias KeyValueRepo<Key,Value> = StandardKeyValueRepo<Key, Value>
class DelegateBasedStandardKeyValueRepo<Key, Value>(
readDelegate: ReadStandardKeyValueRepo<Key, Value>,
writeDelegate: WriteStandardKeyValueRepo<Key, Value>
) : StandardKeyValueRepo<Key, Value>,
ReadStandardKeyValueRepo<Key, Value> by readDelegate,
WriteStandardKeyValueRepo<Key, Value> by writeDelegate

View File

@ -74,19 +74,24 @@ abstract class MapCRUDRepo<ObjectType, IdType, InputValueType>(
fun <ObjectType, IdType, InputValueType> MapCRUDRepo( fun <ObjectType, IdType, InputValueType> MapCRUDRepo(
map: MutableMap<IdType, ObjectType>, map: MutableMap<IdType, ObjectType>,
updateCallback: suspend (newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType, updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
createCallback: suspend (newValue: InputValueType) -> Pair<IdType, ObjectType> createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = object : MapCRUDRepo<ObjectType, IdType, InputValueType>(map) { ) = object : MapCRUDRepo<ObjectType, IdType, InputValueType>(map) {
override suspend fun updateObject( override suspend fun updateObject(
newValue: InputValueType, newValue: InputValueType,
id: IdType, id: IdType,
old: ObjectType old: ObjectType
): ObjectType = updateCallback(newValue, id, old) ): ObjectType = map.updateCallback(newValue, id, old)
override suspend fun createObject(newValue: InputValueType): Pair<IdType, ObjectType> = createCallback(newValue) override suspend fun createObject(newValue: InputValueType): Pair<IdType, ObjectType> = map.createCallback(newValue)
} }
fun <ObjectType, IdType, InputValueType> MapCRUDRepo(
updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = MapCRUDRepo(mutableMapOf(), updateCallback, createCallback)
fun <ObjectType, IdType, InputValueType> MutableMap<IdType, ObjectType>.asCrudRepo( fun <ObjectType, IdType, InputValueType> MutableMap<IdType, ObjectType>.asCrudRepo(
updateCallback: suspend (newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType, updateCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType, id: IdType, old: ObjectType) -> ObjectType,
createCallback: suspend (newValue: InputValueType) -> Pair<IdType, ObjectType> createCallback: suspend MutableMap<IdType, ObjectType>.(newValue: InputValueType) -> Pair<IdType, ObjectType>
) = MapCRUDRepo(this, updateCallback, createCallback) ) = MapCRUDRepo(this, updateCallback, createCallback)

View File

@ -78,11 +78,11 @@ class WriteMapKeyValueRepo<Key, Value>(
} }
override suspend fun unsetWithValues(toUnset: List<Value>) { override suspend fun unsetWithValues(toUnset: List<Value>) {
map.forEach { map.mapNotNull { (k, v) ->
if (it.value in toUnset) { k.takeIf { v in toUnset }
map.remove(it.key) }.forEach {
_onValueRemoved.emit(it.key) map.remove(it)
} _onValueRemoved.emit(it)
} }
} }
} }

View File

@ -5,6 +5,7 @@ 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.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import kotlinx.serialization.KSerializer import kotlinx.serialization.KSerializer
import kotlinx.serialization.builtins.serializer import kotlinx.serialization.builtins.serializer
@ -39,9 +40,7 @@ class KtorReadStandardCrudRepo<ObjectType, IdType> (
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
getByIdRouting, getByIdRouting,
mapOf( idParameterName to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
"id" to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
)
), ),
objectsSerializerNullable objectsSerializerNullable
) )
@ -50,9 +49,7 @@ class KtorReadStandardCrudRepo<ObjectType, IdType> (
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
containsRouting, containsRouting,
mapOf( idParameterName to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
"id" to unifiedRequester.encodeUrlQueryValue(idsSerializer, id)
)
), ),
Boolean.serializer() Boolean.serializer()
) )

View File

@ -4,10 +4,11 @@ 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.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.http.HttpStatusCode import io.ktor.http.*
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.serialization.* import kotlinx.serialization.*
@ -16,48 +17,59 @@ class KtorReadStandardCrudRepoClient<ObjectType, IdType> (
private val baseUrl: String, private val baseUrl: String,
private val httpClient: HttpClient, private val httpClient: HttpClient,
private val objectType: TypeInfo, private val objectType: TypeInfo,
private val contentType: ContentType,
private val idSerializer: suspend (IdType) -> String private val idSerializer: suspend (IdType) -> String
) : ReadStandardCRUDRepo<ObjectType, IdType> { ) : ReadStandardCRUDRepo<ObjectType, IdType> {
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = httpClient.get( override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = httpClient.get(
buildStandardUrl(baseUrl, getByPaginationRouting, pagination.asUrlQueryParts) buildStandardUrl(baseUrl, getByPaginationRouting, pagination.asUrlQueryParts)
).body() ) {
contentType(contentType)
}.body()
override suspend fun getById(id: IdType): ObjectType? = httpClient.get( override suspend fun getById(id: IdType): ObjectType? = httpClient.get(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
getByIdRouting, getByIdRouting,
mapOf( mapOf(
"id" to idSerializer(id) idParameterName to idSerializer(id)
) )
) )
).takeIf { it.status != HttpStatusCode.NoContent } ?.body<ObjectType>(objectType) ) {
contentType(contentType)
}.takeIf { it.status != HttpStatusCode.NoContent } ?.body<ObjectType>(objectType)
override suspend fun contains(id: IdType): Boolean = httpClient.get( override suspend fun contains(id: IdType): Boolean = httpClient.get(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
containsRouting, containsRouting,
mapOf( mapOf(
"id" to idSerializer(id) idParameterName to idSerializer(id)
) )
) )
).body() ) {
contentType(contentType)
}.body()
override suspend fun count(): Long = httpClient.get( override suspend fun count(): Long = httpClient.get(
buildStandardUrl( buildStandardUrl(
baseUrl, baseUrl,
countRouting countRouting
) )
).body() ) {
contentType(contentType)
}.body()
} }
inline fun <reified ObjectType, IdType> KtorReadStandardCrudRepoClient( inline fun <reified ObjectType, IdType> KtorReadStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
httpClient: HttpClient, httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (IdType) -> String noinline idSerializer: suspend (IdType) -> String
) = KtorReadStandardCrudRepoClient<ObjectType, IdType>( ) = KtorReadStandardCrudRepoClient<ObjectType, IdType>(
baseUrl, baseUrl,
httpClient, httpClient,
typeInfo<ObjectType>(), typeInfo<ObjectType>(),
contentType,
idSerializer idSerializer
) )
@ -65,8 +77,9 @@ inline fun <reified ObjectType, IdType> KtorReadStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
httpClient: HttpClient, httpClient: HttpClient,
idsSerializer: KSerializer<IdType>, idsSerializer: KSerializer<IdType>,
serialFormat: StringFormat serialFormat: StringFormat,
) = KtorReadStandardCrudRepoClient<ObjectType, IdType>(baseUrl, httpClient) { contentType: ContentType,
) = KtorReadStandardCrudRepoClient<ObjectType, IdType>(baseUrl, httpClient, contentType) {
serialFormat.encodeToString(idsSerializer, it) serialFormat.encodeToString(idsSerializer, it)
} }
@ -74,7 +87,8 @@ inline fun <reified ObjectType, IdType> KtorReadStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
httpClient: HttpClient, httpClient: HttpClient,
idsSerializer: KSerializer<IdType>, idsSerializer: KSerializer<IdType>,
serialFormat: BinaryFormat serialFormat: BinaryFormat,
) = KtorReadStandardCrudRepoClient<ObjectType, IdType>(baseUrl, httpClient) { contentType: ContentType,
) = KtorReadStandardCrudRepoClient<ObjectType, IdType>(baseUrl, httpClient, contentType) {
serialFormat.encodeHex(idsSerializer, it) serialFormat.encodeHex(idsSerializer, it)
} }

View File

@ -3,91 +3,120 @@ 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.repos.* import dev.inmo.micro_utils.repos.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.utils.EmptyContent.contentType
import io.ktor.http.ContentType
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.serialization.* import kotlinx.serialization.*
class KtorStandardCrudRepoClient<ObjectType, IdType, InputValue> ( class KtorStandardCrudRepoClient<ObjectType, IdType, InputValue> (
baseUrl: String, readDelegate: ReadStandardCRUDRepo<ObjectType, IdType>,
httpClient: HttpClient, writeDelegate: WriteStandardCRUDRepo<ObjectType, IdType, InputValue>
objectTypeInfo: TypeInfo, ) : StandardCRUDRepo<ObjectType, IdType, InputValue> by DelegateBasedStandardCRUDRepo(
idSerializer: suspend (IdType) -> String readDelegate,
) : StandardCRUDRepo<ObjectType, IdType, InputValue>, writeDelegate
ReadStandardCRUDRepo<ObjectType, IdType> by KtorReadStandardCrudRepoClient( ) {
baseUrl, companion object {
httpClient, inline operator fun <reified ObjectType, reified IdType, reified InputValue> invoke(
objectTypeInfo, baseUrl: String,
idSerializer httpClient: HttpClient,
), objectTypeInfo: TypeInfo,
WriteStandardCRUDRepo<ObjectType, IdType, InputValue> by KtorWriteStandardCrudRepoClient( contentType: ContentType,
baseUrl, noinline idSerializer: suspend (IdType) -> String
httpClient ) = KtorStandardCrudRepoClient(
) { KtorReadStandardCrudRepoClient(
constructor( baseUrl,
baseUrl: String, httpClient,
subpart: String, objectTypeInfo,
httpClient: HttpClient, contentType,
objectTypeInfo: TypeInfo, idSerializer
idSerializer: suspend (IdType) -> String ),
) : this( KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue>(
buildStandardUrl(baseUrl, subpart), httpClient, objectTypeInfo, idSerializer baseUrl,
) httpClient,
contentType
)
)
inline operator fun <reified ObjectType, reified IdType, reified InputValue> invoke(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
objectTypeInfo: TypeInfo,
contentType: ContentType,
noinline idSerializer: suspend (IdType) -> String
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(
buildStandardUrl(baseUrl, subpart),
httpClient,
objectTypeInfo,
contentType,
idSerializer
)
}
} }
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient( inline fun <reified ObjectType, reified IdType, reified InputValue> KtorStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
httpClient: HttpClient, httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (IdType) -> String noinline idSerializer: suspend (IdType) -> String
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>( ) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(
baseUrl, baseUrl,
httpClient, httpClient,
typeInfo<ObjectType>(), typeInfo<ObjectType>(),
contentType,
idSerializer idSerializer
) )
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient( inline fun <reified ObjectType, reified IdType, reified InputValue> KtorStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
httpClient: HttpClient, httpClient: HttpClient,
idsSerializer: KSerializer<IdType>, idsSerializer: KSerializer<IdType>,
serialFormat: StringFormat serialFormat: StringFormat,
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(baseUrl, httpClient) { contentType: ContentType,
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(baseUrl, httpClient, contentType) {
serialFormat.encodeToString(idsSerializer, it) serialFormat.encodeToString(idsSerializer, it)
} }
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient( inline fun <reified ObjectType, reified IdType, reified InputValue> KtorStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
httpClient: HttpClient, httpClient: HttpClient,
idsSerializer: KSerializer<IdType>, idsSerializer: KSerializer<IdType>,
serialFormat: BinaryFormat serialFormat: BinaryFormat,
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(baseUrl, httpClient) { contentType: ContentType,
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(baseUrl, httpClient, contentType) {
serialFormat.encodeHex(idsSerializer, it) serialFormat.encodeHex(idsSerializer, it)
} }
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient( inline fun <reified ObjectType, reified IdType, reified InputValue> KtorStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
subpart: String, subpart: String,
httpClient: HttpClient, httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (IdType) -> String noinline idSerializer: suspend (IdType) -> String
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>( ) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(
buildStandardUrl(baseUrl, subpart), buildStandardUrl(baseUrl, subpart),
httpClient, httpClient,
contentType,
idSerializer idSerializer
) )
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient( inline fun <reified ObjectType, reified IdType, reified InputValue> KtorStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
subpart: String, subpart: String,
httpClient: HttpClient, httpClient: HttpClient,
idsSerializer: KSerializer<IdType>, idsSerializer: KSerializer<IdType>,
serialFormat: StringFormat serialFormat: StringFormat,
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(buildStandardUrl(baseUrl, subpart), httpClient, idsSerializer, serialFormat) contentType: ContentType,
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(buildStandardUrl(baseUrl, subpart), httpClient, idsSerializer, serialFormat, contentType)
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient( inline fun <reified ObjectType, reified IdType, reified InputValue> KtorStandardCrudRepoClient(
baseUrl: String, baseUrl: String,
subpart: String, subpart: String,
httpClient: HttpClient, httpClient: HttpClient,
idsSerializer: KSerializer<IdType>, idsSerializer: KSerializer<IdType>,
serialFormat: BinaryFormat serialFormat: BinaryFormat,
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(buildStandardUrl(baseUrl, subpart), httpClient, idsSerializer, serialFormat) contentType: ContentType,
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(buildStandardUrl(baseUrl, subpart), httpClient, idsSerializer, serialFormat, contentType)

View File

@ -8,6 +8,9 @@ import dev.inmo.micro_utils.repos.ktor.common.crud.*
import io.ktor.client.HttpClient import io.ktor.client.HttpClient
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.* import io.ktor.client.request.*
import io.ktor.client.statement.HttpResponse
import io.ktor.http.ContentType
import io.ktor.http.contentType
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
@ -16,36 +19,71 @@ import kotlinx.serialization.builtins.*
class KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue> ( class KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue> (
private val baseUrl: String, private val baseUrl: String,
private val httpClient: HttpClient private val httpClient: HttpClient,
override val newObjectsFlow: Flow<ObjectType>,
override val updatedObjectsFlow: Flow<ObjectType>,
override val deletedObjectsIdsFlow: Flow<IdType>,
private val createSetup: suspend HttpRequestBuilder.(List<InputValue>) -> Unit,
private val updateSetup: suspend HttpRequestBuilder.(List<UpdatedValuePair<IdType, InputValue>>) -> Unit,
private val deleteByIdSetup: suspend HttpRequestBuilder.(List<IdType>) -> Unit,
private val createBodyGetter: suspend HttpResponse.() -> List<ObjectType>,
private val updateBodyGetter: suspend HttpResponse.() -> List<ObjectType>
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValue> { ) : WriteStandardCRUDRepo<ObjectType, IdType, InputValue> {
override suspend fun create(values: List<InputValue>): List<ObjectType> = httpClient.post(
buildStandardUrl(baseUrl, createRouting)
) {
createSetup(values)
}.createBodyGetter()
override val newObjectsFlow: Flow<ObjectType> = httpClient.createStandardWebsocketFlow( override suspend fun update(
buildStandardUrl(baseUrl, newObjectsFlowRouting), values: List<UpdatedValuePair<IdType, InputValue>>
) ): List<ObjectType> = httpClient.post(
override val updatedObjectsFlow: Flow<ObjectType> = httpClient.createStandardWebsocketFlow( buildStandardUrl(baseUrl, updateManyRouting)
buildStandardUrl(baseUrl, updatedObjectsFlowRouting) ) {
) updateSetup(values)
override val deletedObjectsIdsFlow: Flow<IdType> = httpClient.createStandardWebsocketFlow( }.updateBodyGetter()
buildStandardUrl(baseUrl, deletedObjectsIdsFlowRouting)
)
override suspend fun create(values: List<InputValue>): List<ObjectType> = httpClient.post {
url(buildStandardUrl(baseUrl, createRouting))
setBody(values)
}.body()
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValue>>): List<ObjectType> = httpClient.post {
url(buildStandardUrl(baseUrl, updateManyRouting))
setBody(values)
}.body()
override suspend fun update(id: IdType, value: InputValue): ObjectType? = update(listOf(id to value)).firstOrNull() override suspend fun update(id: IdType, value: InputValue): ObjectType? = update(listOf(id to value)).firstOrNull()
override suspend fun deleteById(ids: List<IdType>) { override suspend fun deleteById(ids: List<IdType>) {
httpClient.post { httpClient.post(
url(buildStandardUrl(baseUrl, deleteByIdRouting)) buildStandardUrl(baseUrl, deleteByIdRouting)
setBody(ids) ) {
} deleteByIdSetup(ids)
}.status
}
companion object {
inline operator fun <reified ObjectType, reified IdType, reified InputValue> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType
) = KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue>(
baseUrl,
httpClient,
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, newObjectsFlowRouting),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, updatedObjectsFlowRouting),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, deletedObjectsIdsFlowRouting),
),
{
contentType(contentType)
setBody(it)
},
{
contentType(contentType)
setBody(it)
},
{
contentType(contentType)
setBody(it)
},
{ body() },
{ body() }
)
} }
} }

View File

@ -0,0 +1,144 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.common.*
import dev.inmo.micro_utils.pagination.*
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
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.one_to_many.containsByKeyRoute
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 KtorReadStandardKeyValueRepoClient<Key, Value>(
private val baseUrl: String,
private val httpClient: HttpClient,
private val contentType: ContentType,
private val objectType: TypeInfo,
private val paginationResultObjectsTypeInfo: TypeInfo,
private val paginationResultIdsTypeInfo: TypeInfo,
private val idSerializer: suspend (Key) -> String,
private val valueSerializer: suspend (Value) -> String
) : ReadStandardKeyValueRepo<Key, Value> {
override suspend fun get(k: Key): Value? = httpClient.get(
buildStandardUrl(
baseUrl,
getRoute,
mapOf(
idParameterName to idSerializer(k)
)
)
) {
contentType(contentType)
}.takeIf { it.status != HttpStatusCode.NoContent } ?.body<Value>(objectType)
override suspend fun contains(key: Key): Boolean = httpClient.get(
buildStandardUrl(
baseUrl,
containsRoute,
idParameterName to idSerializer(key)
)
) {
contentType(contentType)
}.body()
override suspend fun values(
pagination: Pagination,
reversed: Boolean
): PaginationResult<Value> = httpClient.get(
buildStandardUrl(baseUrl, valuesRoute, pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()))
) {
contentType(contentType)
}.body(paginationResultObjectsTypeInfo)
override suspend fun keys(
pagination: Pagination,
reversed: Boolean
): PaginationResult<Key> = httpClient.get(
buildStandardUrl(baseUrl, keysRoute, pagination.asUrlQueryParts + (reversedParameterName to reversed.toString()))
) {
contentType(contentType)
}.body(paginationResultIdsTypeInfo)
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(paginationResultIdsTypeInfo)
override suspend fun count(): Long = httpClient.get(
buildStandardUrl(
baseUrl,
countRouting
)
) {
contentType(contentType)
}.body()
}
inline fun <reified Key, reified Value> KtorReadStandardKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorReadStandardKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
typeInfo<Value>(),
typeInfo<PaginationResult<Value>>(),
typeInfo<PaginationResult<Key>>(),
idSerializer,
valueSerializer
)
inline fun <reified Key, reified Value> KtorReadStandardKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<Key>,
valueSerializer: KSerializer<Value>,
serialFormat: StringFormat,
contentType: ContentType,
) = KtorReadStandardKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeToString(idsSerializer, it).encodeURLQueryComponent()
}
) {
serialFormat.encodeToString(valueSerializer, it).encodeURLQueryComponent()
}
inline fun <reified Key, reified Value> KtorReadStandardKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
idsSerializer: KSerializer<Key>,
valuesSerializer: KSerializer<Value>,
serialFormat: BinaryFormat,
contentType: ContentType,
) = KtorReadStandardKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeHex(idsSerializer, it)
}
) {
serialFormat.encodeHex(valuesSerializer, it)
}

View File

@ -0,0 +1,85 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value
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 KtorStandardKeyValueRepoClient<Key, Value> (
readDelegate: ReadStandardKeyValueRepo<Key, Value>,
writeDelegate: WriteStandardKeyValueRepo<Key, Value>
) : StandardKeyValueRepo<Key, Value> by DelegateBasedStandardKeyValueRepo(
readDelegate,
writeDelegate
) {
companion object {
inline operator fun <reified Key, reified Value> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorStandardKeyValueRepoClient(
KtorReadStandardKeyValueRepoClient(
baseUrl, httpClient, contentType, idSerializer, valueSerializer
),
KtorWriteStandardKeyValueRepoClient(
baseUrl,
httpClient,
contentType
)
)
inline operator fun <reified Key, reified Value> invoke(
baseUrl: String,
subpart: String,
httpClient: HttpClient,
contentType: ContentType,
noinline idSerializer: suspend (Key) -> String,
noinline valueSerializer: suspend (Value) -> String
) = KtorStandardKeyValueRepoClient(
buildStandardUrl(baseUrl, subpart),
httpClient,
contentType,
idSerializer,
valueSerializer
)
}
}
inline fun <reified Key, reified Value> KtorStandardKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
idSerializer: SerializationStrategy<Key>,
valueSerializer: SerializationStrategy<Value>,
serialFormat: StringFormat,
) = KtorStandardKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeToString(idSerializer, it).encodeURLQueryComponent()
}
) {
serialFormat.encodeToString(valueSerializer, it).encodeURLQueryComponent()
}
inline fun <reified Key, reified Value> KtorStandardKeyValueRepoClient(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType,
idSerializer: SerializationStrategy<Key>,
valueSerializer: SerializationStrategy<Value>,
serialFormat: BinaryFormat,
) = KtorStandardKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
{
serialFormat.encodeHex(idSerializer, it)
}
) {
serialFormat.encodeHex(valueSerializer, it)
}

View File

@ -0,0 +1,83 @@
package dev.inmo.micro_utils.repos.ktor.client.key_value
import dev.inmo.micro_utils.ktor.client.createStandardWebsocketFlow
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.ktor.common.*
import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import io.ktor.client.HttpClient
import io.ktor.client.request.get
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
import kotlinx.serialization.*
class KtorWriteStandardKeyValueRepoClient<Key, Value>(
private val baseUrl: String,
private val httpClient: HttpClient,
private val contentType: ContentType,
override val onNewValue: Flow<Pair<Key, Value>>,
override val onValueRemoved: Flow<Key>,
private val idsListTypeInfo: TypeInfo,
private val objectsListTypeInfo: TypeInfo,
private val idsToObjectsMapTypeInfo: TypeInfo
) : WriteStandardKeyValueRepo<Key, Value> {
@OptIn(InternalAPI::class)
override suspend fun unsetWithValues(toUnset: List<Value>) {
httpClient.post(
buildStandardUrl(baseUrl, unsetWithValuesRoute)
) {
body = toUnset
bodyType = objectsListTypeInfo
contentType(contentType)
}.status
}
@OptIn(InternalAPI::class)
override suspend fun unset(toUnset: List<Key>) {
httpClient.post(
buildStandardUrl(baseUrl, unsetRoute)
) {
body = toUnset
bodyType = idsListTypeInfo
contentType(contentType)
}.status
}
@OptIn(InternalAPI::class)
override suspend fun set(toSet: Map<Key, Value>) {
httpClient.post(
buildStandardUrl(baseUrl, setRoute)
) {
body = toSet
bodyType = idsToObjectsMapTypeInfo
contentType(contentType)
}.status
}
companion object {
inline operator fun <reified Key, reified Value> invoke(
baseUrl: String,
httpClient: HttpClient,
contentType: ContentType
) = KtorWriteStandardKeyValueRepoClient<Key, Value>(
baseUrl,
httpClient,
contentType,
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onNewValueRoute),
),
httpClient.createStandardWebsocketFlow(
buildStandardUrl(baseUrl, onValueRemovedRoute),
),
typeInfo<List<Key>>(),
typeInfo<List<Value>>(),
typeInfo<Map<Key, Value>>()
)
}
}

View File

@ -13,5 +13,22 @@ kotlin {
api internalProject("micro_utils.repos.common") api internalProject("micro_utils.repos.common")
} }
} }
jvmTest {
dependencies {
implementation internalProject("micro_utils.repos.common")
implementation internalProject("micro_utils.repos.ktor.client")
implementation internalProject("micro_utils.repos.ktor.server")
implementation internalProject("micro_utils.repos.inmemory")
implementation libs.kt.coroutines.test
implementation libs.ktor.server.cio
implementation libs.ktor.client.cio
implementation libs.ktor.server.content.negotiation
implementation libs.ktor.serialization.kotlinx.json
implementation libs.ktor.client.content.negotiation
implementation libs.ktor.client.logging
implementation libs.ktor.client.websockets
}
}
} }
} }

View File

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

View File

@ -0,0 +1,89 @@
import dev.inmo.micro_utils.repos.*
import dev.inmo.micro_utils.repos.ktor.client.crud.KtorStandardCrudRepoClient
import dev.inmo.micro_utils.repos.ktor.server.crud.configureStandardCrudRepoRoutes
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.json.Json
import kotlin.test.Test
import kotlin.test.assertEquals
class CRUDTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCRUDFunctions() {
runTest() {
val map = mutableMapOf<Int, ComplexData>()
val repo = MapCRUDRepo<ComplexData, Int, SimpleData>(
map,
{ newValue, id, oldValue ->
oldValue.copy(title = newValue.title)
}
) {
size to ComplexData(size, title = it.title)
}
val server = io.ktor.server.engine.embeddedServer(
CIO,
23456,
"127.0.0.1"
) {
install(ContentNegotiation) {
json()
}
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
routing {
configureStandardCrudRepoRoutes(
repo
) {
it.toInt()
}
}
}.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 = KtorStandardCrudRepoClient<ComplexData, Int, SimpleData>(
"http://127.0.0.1:23456",
client,
ContentType.Application.Json
) {
it.toString()
}
val created = crudClient.create(SimpleData("Example")).single()
assertEquals(map.size, 1)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(1, crudClient.count())
assertEquals(map.getValue(map.keys.first()), created)
val updated = crudClient.update(created.id, SimpleData("Example2"))
assertEquals(map.size, 1)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(1, crudClient.count())
assertEquals(map.getValue(map.keys.first()), updated)
crudClient.deleteById(created.id)
assertEquals(map.size, 0)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(0, crudClient.count())
server.stop()
}
}
}

View File

@ -0,0 +1,10 @@
import com.benasher44.uuid.uuid4
import kotlinx.serialization.Serializable
@Serializable
data class ComplexData(
val id: Int,
val simple: SimpleData = SimpleData(),
val simples: List<SimpleData> = (0 until 100).map { SimpleData(("$it")) },
val title: String = uuid4().toString()
)

View File

@ -0,0 +1,139 @@
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.server.key_value.configureStandardKeyValueRepoRoutes
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 KVTests {
@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun testCRUDFunctions() {
runTest() {
val map = mutableMapOf<Int, ComplexData>()
val repo = MapKeyValueRepo<Int, ComplexData>(map)
val server = io.ktor.server.engine.embeddedServer(
CIO,
23456,
"127.0.0.1"
) {
install(ContentNegotiation) {
json()
}
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
routing {
configureStandardKeyValueRepoRoutes(
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 = KtorStandardKeyValueRepoClient<Int, ComplexData>(
"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 dataInOneKey
) + (0 until repeatCount).map {
(it + 2) to dataInMultipleKeys
}
dataList.forEachIndexed { i, (id, data) ->
crudClient.set(id, data)
assertEquals(map.size, i + 1)
assertEquals(map.size.toLong(), crudClient.count())
assertEquals(i + 1L, crudClient.count())
dataList.take(i + 1).forEach { (id, data) ->
assertEquals(data, map[id])
assertEquals(data, crudClient.get(id))
assertEquals(map[id], crudClient.get(id))
}
}
dataList.forEach { (id, data) ->
assertTrue(crudClient.contains(id))
assertEquals(data, crudClient.get(id))
}
assertEquals(
dataList.mapNotNull { if (it.second == dataInMultipleKeys) it.first else null },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.keys(dataInMultipleKeys, it)
}
)
assertEquals(
dataList.mapNotNull { if (it.second == 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.second },
getAllWithNextPaging(firstPageWithOneElementPagination) {
crudClient.values(it)
}
)
assertEquals(dataList.size.toLong(), crudClient.count())
crudClient.unsetWithValues(dataInMultipleKeys)
assertEquals(
dataList.filter { it.second == dataInOneKey }.size.toLong(),
crudClient.count()
)
crudClient.unset(dataList.first { it.second == dataInOneKey }.first)
assertEquals(
0,
crudClient.count()
)
server.stop()
}
}
}

View File

@ -0,0 +1,7 @@
import com.benasher44.uuid.uuid4
import kotlinx.serialization.Serializable
@Serializable
data class SimpleData(
val title: String = uuid4().toString()
)

View File

@ -5,6 +5,7 @@ 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.crud.* import dev.inmo.micro_utils.repos.ktor.common.crud.*
import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import io.ktor.http.ContentType import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.server.application.call import io.ktor.server.application.call
@ -25,7 +26,7 @@ inline fun <reified ObjectType, reified IdType> Route.configureReadStandardCrudR
get(getByIdRouting) { get(getByIdRouting) {
val id = idDeserializer( val id = idDeserializer(
call.getQueryParameterOrSendError("id") ?: return@get call.getQueryParameterOrSendError(idParameterName) ?: return@get
) )
val result = originalRepo.getById(id) val result = originalRepo.getById(id)
@ -39,7 +40,7 @@ inline fun <reified ObjectType, reified IdType> Route.configureReadStandardCrudR
get(containsRouting) { get(containsRouting) {
val id = idDeserializer( val id = idDeserializer(
call.getQueryParameterOrSendError("id") ?: return@get call.getQueryParameterOrSendError(idParameterName) ?: return@get
) )
call.respond( call.respond(

View File

@ -26,11 +26,7 @@ inline fun <reified ObjectType : Any, reified IdType : Any, reified InputValue :
) )
post(createRouting) { post(createRouting) {
call.respond( call.respond(originalRepo.create(call.receive()))
originalRepo.create(
call.receive()
)
)
} }
post(updateManyRouting) { post(updateManyRouting) {

View File

@ -0,0 +1,58 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value
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.StandardCRUDRepo
import dev.inmo.micro_utils.repos.StandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName
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 kotlinx.serialization.*
import kotlinx.serialization.builtins.serializer
inline fun <reified Key : Any, reified Value : Any> Route.configureStandardKeyValueRepoRoutes (
originalRepo: StandardKeyValueRepo<Key, Value>,
noinline idDeserializer: suspend (String) -> Key,
noinline valueDeserializer: suspend (String) -> Value
) {
configureReadStandardKeyValueRepoRoutes(originalRepo, idDeserializer, valueDeserializer)
configureWriteStandardKeyValueRepoRoutes(originalRepo)
}
inline fun <reified Key : Any, reified Value : Any> Route.configureStandardKeyValueRepoRoutes(
originalRepo: StandardKeyValueRepo<Key, Value>,
idsSerializer: DeserializationStrategy<Key>,
valueSerializer: DeserializationStrategy<Value>,
serialFormat: StringFormat
) = configureStandardKeyValueRepoRoutes(
originalRepo,
{
serialFormat.decodeFromString(idsSerializer, it.decodeURLQueryComponent())
},
{
serialFormat.decodeFromString(valueSerializer, it.decodeURLQueryComponent())
}
)
inline fun <reified Key : Any, reified Value : Any> Route.configureStandardKeyValueRepoRoutes(
originalRepo: StandardKeyValueRepo<Key, Value>,
idsSerializer: DeserializationStrategy<Key>,
valueSerializer: DeserializationStrategy<Value>,
serialFormat: BinaryFormat
) = configureStandardKeyValueRepoRoutes(
originalRepo,
{
serialFormat.decodeHex(idsSerializer, it)
},
{
serialFormat.decodeHex(valueSerializer, it)
}
)

View File

@ -0,0 +1,104 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value
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.ReadStandardCRUDRepo
import dev.inmo.micro_utils.repos.ReadStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.idParameterName
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
import dev.inmo.micro_utils.repos.ktor.common.valueParameterName
import io.ktor.http.*
import io.ktor.server.application.call
import io.ktor.server.response.respond
import io.ktor.server.response.responseType
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.*
import kotlinx.serialization.builtins.serializer
@OptIn(InternalAPI::class)
inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoRoutes (
originalRepo: ReadStandardKeyValueRepo<Key, Value>,
noinline idDeserializer: suspend (String) -> Key,
noinline valueDeserializer: suspend (String) -> Value
) {
get(getRoute) {
val key = idDeserializer(
call.getQueryParameterOrSendError(idParameterName) ?: return@get
)
originalRepo.get(key) ?.let {
call.respond(it)
} ?: call.respond(HttpStatusCode.NoContent)
}
val paginationWithValuesTypeInfo = typeInfo<PaginationResult<Value>>()
get(valuesRoute) {
val pagination = call.request.queryParameters.extractPagination
val reversed = call.getQueryParameter(reversedParameterName) ?.toBoolean() ?: false
call.response.responseType = paginationWithValuesTypeInfo
call.response.pipeline.execute(call, originalRepo.values(pagination, reversed) as Any)
}
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)
)
}
get(containsRoute) {
val key = idDeserializer(
call.getQueryParameterOrSendError(idParameterName) ?: return@get
)
call.respond(
originalRepo.contains(key)
)
}
get(countRoute) {
call.respond(originalRepo.count())
}
}
inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoRoutes(
originalRepo: ReadStandardKeyValueRepo<Key, Value>,
idsSerializer: DeserializationStrategy<Key>,
valueSerializer: DeserializationStrategy<Value>,
serialFormat: StringFormat
) = configureReadStandardKeyValueRepoRoutes(
originalRepo,
{
serialFormat.decodeFromString(idsSerializer, it.decodeURLQueryComponent())
},
{
serialFormat.decodeFromString(valueSerializer, it.decodeURLQueryComponent())
}
)
inline fun <reified Key, reified Value> Route.configureReadStandardKeyValueRepoRoutes(
originalRepo: ReadStandardKeyValueRepo<Key, Value>,
idsSerializer: DeserializationStrategy<Key>,
valueSerializer: DeserializationStrategy<Value>,
serialFormat: BinaryFormat
) = configureReadStandardKeyValueRepoRoutes(
originalRepo,
{
serialFormat.decodeHex(idsSerializer, it)
},
{
serialFormat.decodeHex(valueSerializer, it)
}
)

View File

@ -0,0 +1,45 @@
package dev.inmo.micro_utils.repos.ktor.server.key_value
import dev.inmo.micro_utils.ktor.server.*
import dev.inmo.micro_utils.repos.WriteStandardKeyValueRepo
import dev.inmo.micro_utils.repos.ktor.common.key_value.*
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.configureWriteStandardKeyValueRepoRoutes (
originalRepo: WriteStandardKeyValueRepo<Key, Value>
) {
includeWebsocketHandling(
onNewValueRoute,
originalRepo.onNewValue
)
includeWebsocketHandling(
onValueRemovedRoute,
originalRepo.onValueRemoved
)
val mapType = typeInfo<Map<Key, Value>>()
val listKeysType = typeInfo<List<Key>>()
val listValuesType = typeInfo<List<Value>>()
post(setRoute) {
originalRepo.set(call.receive(mapType))
call.respond(HttpStatusCode.OK)
}
post(unsetRoute) {
originalRepo.unset(call.receive(listKeysType))
call.respond(HttpStatusCode.OK)
}
post(unsetWithValuesRoute) {
originalRepo.unsetWithValues(call.receive(listValuesType))
call.respond(HttpStatusCode.OK)
}
}