mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2024-12-22 16:47:15 +00:00
start improvements in ktor parts
This commit is contained in:
parent
0e4a63057f
commit
8eed435302
@ -1,5 +1,7 @@
|
||||
# Changelog
|
||||
|
||||
## 0.11.0
|
||||
|
||||
## 0.10.8
|
||||
|
||||
* `Common`
|
||||
|
@ -0,0 +1,54 @@
|
||||
package dev.inmo.micro_utils.ktor.client
|
||||
|
||||
import dev.inmo.micro_utils.coroutines.runCatchingSafely
|
||||
import dev.inmo.micro_utils.coroutines.safely
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.plugins.pluginOrNull
|
||||
import io.ktor.client.plugins.websocket.*
|
||||
import io.ktor.client.request.HttpRequestBuilder
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readBytes
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.serialization.DeserializationStrategy
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
inline fun <T> HttpClient.createStandardWebsocketFlow(
|
||||
url: String,
|
||||
crossinline checkReconnection: suspend (Throwable?) -> Boolean = { true },
|
||||
noinline requestBuilder: HttpRequestBuilder.() -> Unit = {}
|
||||
): Flow<T> {
|
||||
pluginOrNull(WebSockets) ?: error("Plugin $WebSockets must be installed for using createStandardWebsocketFlow")
|
||||
|
||||
val correctedUrl = url.asCorrectWebSocketUrl
|
||||
|
||||
return channelFlow {
|
||||
do {
|
||||
val reconnect = runCatchingSafely {
|
||||
ws(correctedUrl, requestBuilder) {
|
||||
for (received in incoming) {
|
||||
sendSerialized(received.data)
|
||||
}
|
||||
}
|
||||
checkReconnection(null)
|
||||
}.getOrElse { e ->
|
||||
checkReconnection(e).also {
|
||||
if (!it) {
|
||||
close(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (reconnect && isActive)
|
||||
|
||||
if (isActive) {
|
||||
safely {
|
||||
close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package dev.inmo.micro_utils.ktor.server
|
||||
|
||||
import dev.inmo.micro_utils.coroutines.safely
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.server.application.install
|
||||
import io.ktor.server.application.pluginOrNull
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.application
|
||||
import io.ktor.server.websocket.*
|
||||
import io.ktor.websocket.send
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.SerializationStrategy
|
||||
|
||||
inline fun <reified T : Any> Route.includeWebsocketHandling(
|
||||
suburl: String,
|
||||
flow: Flow<T>,
|
||||
protocol: URLProtocol? = null
|
||||
) {
|
||||
application.apply {
|
||||
pluginOrNull(WebSockets) ?: install(WebSockets)
|
||||
}
|
||||
webSocket(suburl, protocol ?.name) {
|
||||
safely {
|
||||
flow.collect {
|
||||
sendSerialized(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ import io.ktor.client.HttpClient
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.serializer
|
||||
|
||||
@Deprecated("Use KtorReadStandardCrudRepoClient instead")
|
||||
class KtorReadStandardCrudRepo<ObjectType, IdType> (
|
||||
private val baseUrl: String,
|
||||
private val unifiedRequester: UnifiedRequester,
|
||||
|
@ -0,0 +1,80 @@
|
||||
package dev.inmo.micro_utils.repos.ktor.client.crud
|
||||
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
import dev.inmo.micro_utils.pagination.*
|
||||
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
|
||||
import dev.inmo.micro_utils.repos.ktor.common.crud.*
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.util.reflect.TypeInfo
|
||||
import io.ktor.util.reflect.typeInfo
|
||||
import kotlinx.serialization.*
|
||||
|
||||
class KtorReadStandardCrudRepoClient<ObjectType, IdType> (
|
||||
private val baseUrl: String,
|
||||
private val httpClient: HttpClient,
|
||||
private val objectType: TypeInfo,
|
||||
private val idSerializer: suspend (IdType) -> String
|
||||
) : ReadStandardCRUDRepo<ObjectType, IdType> {
|
||||
override suspend fun getByPagination(pagination: Pagination): PaginationResult<ObjectType> = httpClient.get(
|
||||
buildStandardUrl(baseUrl, getByPaginationRouting, pagination.asUrlQueryParts)
|
||||
).body()
|
||||
|
||||
override suspend fun getById(id: IdType): ObjectType? = httpClient.get(
|
||||
buildStandardUrl(
|
||||
baseUrl,
|
||||
getByIdRouting,
|
||||
mapOf(
|
||||
"id" to idSerializer(id)
|
||||
)
|
||||
)
|
||||
).takeIf { it.status != HttpStatusCode.NoContent } ?.body<ObjectType>(objectType)
|
||||
|
||||
override suspend fun contains(id: IdType): Boolean = httpClient.get(
|
||||
buildStandardUrl(
|
||||
baseUrl,
|
||||
containsRouting,
|
||||
mapOf(
|
||||
"id" to idSerializer(id)
|
||||
)
|
||||
)
|
||||
).body()
|
||||
|
||||
override suspend fun count(): Long = httpClient.get(
|
||||
buildStandardUrl(
|
||||
baseUrl,
|
||||
countRouting
|
||||
)
|
||||
).body()
|
||||
}
|
||||
|
||||
inline fun <reified ObjectType, IdType> KtorReadStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
httpClient: HttpClient,
|
||||
noinline idSerializer: suspend (IdType) -> String
|
||||
) = KtorReadStandardCrudRepoClient<ObjectType, IdType>(
|
||||
baseUrl,
|
||||
httpClient,
|
||||
typeInfo<ObjectType>(),
|
||||
idSerializer
|
||||
)
|
||||
|
||||
inline fun <reified ObjectType, IdType> KtorReadStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
httpClient: HttpClient,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: StringFormat
|
||||
) = KtorReadStandardCrudRepoClient<ObjectType, IdType>(baseUrl, httpClient) {
|
||||
serialFormat.encodeToString(idsSerializer, it)
|
||||
}
|
||||
|
||||
inline fun <reified ObjectType, IdType> KtorReadStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
httpClient: HttpClient,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: BinaryFormat
|
||||
) = KtorReadStandardCrudRepoClient<ObjectType, IdType>(baseUrl, httpClient) {
|
||||
serialFormat.encodeHex(idsSerializer, it)
|
||||
}
|
@ -7,6 +7,7 @@ import dev.inmo.micro_utils.repos.*
|
||||
import io.ktor.client.HttpClient
|
||||
import kotlinx.serialization.KSerializer
|
||||
|
||||
@Deprecated("Use KtorStandardCrudRepoClient instead")
|
||||
class KtorStandardCrudRepo<ObjectType, IdType, InputValue> (
|
||||
baseUrl: String,
|
||||
baseSubpart: String,
|
||||
|
@ -0,0 +1,93 @@
|
||||
package dev.inmo.micro_utils.repos.ktor.client.crud
|
||||
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
import dev.inmo.micro_utils.repos.*
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.util.reflect.TypeInfo
|
||||
import io.ktor.util.reflect.typeInfo
|
||||
import kotlinx.serialization.*
|
||||
|
||||
class KtorStandardCrudRepoClient<ObjectType, IdType, InputValue> (
|
||||
baseUrl: String,
|
||||
httpClient: HttpClient,
|
||||
objectTypeInfo: TypeInfo,
|
||||
idSerializer: suspend (IdType) -> String
|
||||
) : StandardCRUDRepo<ObjectType, IdType, InputValue>,
|
||||
ReadStandardCRUDRepo<ObjectType, IdType> by KtorReadStandardCrudRepoClient(
|
||||
baseUrl,
|
||||
httpClient,
|
||||
objectTypeInfo,
|
||||
idSerializer
|
||||
),
|
||||
WriteStandardCRUDRepo<ObjectType, IdType, InputValue> by KtorWriteStandardCrudRepoClient(
|
||||
baseUrl,
|
||||
httpClient
|
||||
) {
|
||||
constructor(
|
||||
baseUrl: String,
|
||||
subpart: String,
|
||||
httpClient: HttpClient,
|
||||
objectTypeInfo: TypeInfo,
|
||||
idSerializer: suspend (IdType) -> String
|
||||
) : this(
|
||||
buildStandardUrl(baseUrl, subpart), httpClient, objectTypeInfo, idSerializer
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
httpClient: HttpClient,
|
||||
noinline idSerializer: suspend (IdType) -> String
|
||||
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(
|
||||
baseUrl,
|
||||
httpClient,
|
||||
typeInfo<ObjectType>(),
|
||||
idSerializer
|
||||
)
|
||||
|
||||
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
httpClient: HttpClient,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: StringFormat
|
||||
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(baseUrl, httpClient) {
|
||||
serialFormat.encodeToString(idsSerializer, it)
|
||||
}
|
||||
|
||||
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
httpClient: HttpClient,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: BinaryFormat
|
||||
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(baseUrl, httpClient) {
|
||||
serialFormat.encodeHex(idsSerializer, it)
|
||||
}
|
||||
|
||||
|
||||
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
subpart: String,
|
||||
httpClient: HttpClient,
|
||||
noinline idSerializer: suspend (IdType) -> String
|
||||
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(
|
||||
buildStandardUrl(baseUrl, subpart),
|
||||
httpClient,
|
||||
idSerializer
|
||||
)
|
||||
|
||||
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
subpart: String,
|
||||
httpClient: HttpClient,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: StringFormat
|
||||
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(buildStandardUrl(baseUrl, subpart), httpClient, idsSerializer, serialFormat)
|
||||
|
||||
inline fun <reified ObjectType, IdType, InputValue> KtorStandardCrudRepoClient(
|
||||
baseUrl: String,
|
||||
subpart: String,
|
||||
httpClient: HttpClient,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: BinaryFormat
|
||||
) = KtorStandardCrudRepoClient<ObjectType, IdType, InputValue>(buildStandardUrl(baseUrl, subpart), httpClient, idsSerializer, serialFormat)
|
@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.*
|
||||
|
||||
@Deprecated("Use KtorWriteStandardCrudRepoClient instead")
|
||||
class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
|
||||
private val baseUrl: String,
|
||||
private val unifiedRequester: UnifiedRequester,
|
||||
|
@ -0,0 +1,51 @@
|
||||
package dev.inmo.micro_utils.repos.ktor.client.crud
|
||||
|
||||
import dev.inmo.micro_utils.ktor.client.*
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
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 io.ktor.client.call.body
|
||||
import io.ktor.client.request.*
|
||||
import io.ktor.util.reflect.TypeInfo
|
||||
import io.ktor.util.reflect.typeInfo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.serialization.KSerializer
|
||||
import kotlinx.serialization.builtins.*
|
||||
|
||||
class KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue> (
|
||||
private val baseUrl: String,
|
||||
private val httpClient: HttpClient
|
||||
) : WriteStandardCRUDRepo<ObjectType, IdType, InputValue> {
|
||||
|
||||
override val newObjectsFlow: Flow<ObjectType> = httpClient.createStandardWebsocketFlow(
|
||||
buildStandardUrl(baseUrl, newObjectsFlowRouting),
|
||||
)
|
||||
override val updatedObjectsFlow: Flow<ObjectType> = httpClient.createStandardWebsocketFlow(
|
||||
buildStandardUrl(baseUrl, updatedObjectsFlowRouting)
|
||||
)
|
||||
override val deletedObjectsIdsFlow: Flow<IdType> = httpClient.createStandardWebsocketFlow(
|
||||
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 deleteById(ids: List<IdType>) {
|
||||
httpClient.post {
|
||||
url(buildStandardUrl(baseUrl, deleteByIdRouting))
|
||||
setBody(ids)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package dev.inmo.micro_utils.repos.ktor.server.crud
|
||||
|
||||
import dev.inmo.micro_utils.ktor.common.decodeHex
|
||||
import dev.inmo.micro_utils.ktor.server.*
|
||||
import dev.inmo.micro_utils.pagination.extractPagination
|
||||
import dev.inmo.micro_utils.repos.ReadStandardCRUDRepo
|
||||
import dev.inmo.micro_utils.repos.ktor.common.crud.*
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.http.HttpStatusCode
|
||||
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.*
|
||||
|
||||
inline fun <reified ObjectType, reified IdType> Route.configureReadStandardCrudRepoRoutes(
|
||||
originalRepo: ReadStandardCRUDRepo<ObjectType, IdType>,
|
||||
noinline idDeserializer: suspend (String) -> IdType
|
||||
) {
|
||||
get(getByPaginationRouting) {
|
||||
val pagination = call.request.queryParameters.extractPagination
|
||||
|
||||
call.respond(originalRepo.getByPagination(pagination))
|
||||
}
|
||||
|
||||
get(getByIdRouting) {
|
||||
val id = idDeserializer(
|
||||
call.getQueryParameterOrSendError("id") ?: return@get
|
||||
)
|
||||
|
||||
val result = originalRepo.getById(id)
|
||||
|
||||
if (result == null) {
|
||||
call.respond(HttpStatusCode.NoContent)
|
||||
} else {
|
||||
call.respond(result)
|
||||
}
|
||||
}
|
||||
|
||||
get(containsRouting) {
|
||||
val id = idDeserializer(
|
||||
call.getQueryParameterOrSendError("id") ?: return@get
|
||||
)
|
||||
|
||||
call.respond(
|
||||
originalRepo.contains(id)
|
||||
)
|
||||
}
|
||||
|
||||
get(countRouting) {
|
||||
call.respond(
|
||||
originalRepo.count()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified ObjectType, reified IdType> Route.configureReadStandardCrudRepoRoutes(
|
||||
originalRepo: ReadStandardCRUDRepo<ObjectType, IdType>,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: StringFormat
|
||||
) = configureReadStandardCrudRepoRoutes(originalRepo) {
|
||||
serialFormat.decodeFromString(idsSerializer, it)
|
||||
}
|
||||
|
||||
inline fun <reified ObjectType, reified IdType> Route.configureReadStandardCrudRepoRoutes(
|
||||
originalRepo: ReadStandardCRUDRepo<ObjectType, IdType>,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: BinaryFormat
|
||||
) = configureReadStandardCrudRepoRoutes(originalRepo) {
|
||||
serialFormat.decodeHex(idsSerializer, it)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package dev.inmo.micro_utils.repos.ktor.server.crud
|
||||
|
||||
import dev.inmo.micro_utils.ktor.common.*
|
||||
import dev.inmo.micro_utils.ktor.server.UnifiedRouter
|
||||
import dev.inmo.micro_utils.ktor.server.standardKtorSerialFormatContentType
|
||||
import dev.inmo.micro_utils.repos.StandardCRUDRepo
|
||||
import io.ktor.http.ContentType
|
||||
import io.ktor.server.routing.Route
|
||||
import io.ktor.server.routing.route
|
||||
import kotlinx.serialization.*
|
||||
|
||||
inline fun <reified ObjectType : Any, reified IdType : Any, reified InputValue : Any> Route.configureStandardCrudRepoRoutes(
|
||||
originalRepo: StandardCRUDRepo<ObjectType, IdType, InputValue>,
|
||||
noinline idDeserializer: suspend (String) -> IdType
|
||||
) {
|
||||
configureReadStandardCrudRepoRoutes(originalRepo, idDeserializer)
|
||||
configureWriteStandardCrudRepoRoutes(originalRepo)
|
||||
}
|
||||
|
||||
inline fun <reified ObjectType : Any, reified IdType : Any, reified InputValue : Any> Route.configureStandardCrudRepoRoutes(
|
||||
originalRepo: StandardCRUDRepo<ObjectType, IdType, InputValue>,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: StringFormat
|
||||
) = configureStandardCrudRepoRoutes(originalRepo) {
|
||||
serialFormat.decodeFromString(idsSerializer, it)
|
||||
}
|
||||
|
||||
inline fun <reified ObjectType : Any, reified IdType : Any, reified InputValue : Any> Route.configureStandardCrudRepoRoutes(
|
||||
originalRepo: StandardCRUDRepo<ObjectType, IdType, InputValue>,
|
||||
idsSerializer: KSerializer<IdType>,
|
||||
serialFormat: BinaryFormat
|
||||
) = configureStandardCrudRepoRoutes(originalRepo) {
|
||||
serialFormat.decodeHex(idsSerializer, it)
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package dev.inmo.micro_utils.repos.ktor.server.crud
|
||||
|
||||
import dev.inmo.micro_utils.ktor.server.*
|
||||
import dev.inmo.micro_utils.repos.WriteStandardCRUDRepo
|
||||
import dev.inmo.micro_utils.repos.ktor.common.crud.*
|
||||
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
|
||||
|
||||
inline fun <reified ObjectType : Any, reified IdType : Any, reified InputValue : Any> Route.configureWriteStandardCrudRepoRoutes(
|
||||
originalRepo: WriteStandardCRUDRepo<ObjectType, IdType, InputValue>
|
||||
) {
|
||||
includeWebsocketHandling(
|
||||
newObjectsFlowRouting,
|
||||
originalRepo.newObjectsFlow,
|
||||
)
|
||||
includeWebsocketHandling(
|
||||
updatedObjectsFlowRouting,
|
||||
originalRepo.updatedObjectsFlow
|
||||
)
|
||||
includeWebsocketHandling(
|
||||
deletedObjectsIdsFlowRouting,
|
||||
originalRepo.deletedObjectsIdsFlow
|
||||
)
|
||||
|
||||
post(createRouting) {
|
||||
call.respond(
|
||||
originalRepo.create(
|
||||
call.receive()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
post(updateManyRouting) {
|
||||
call.respond(originalRepo.update(call.receive()))
|
||||
}
|
||||
|
||||
post(deleteByIdRouting) {
|
||||
call.respond(originalRepo.deleteById(call.receive()))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user