add new started clients for crud

This commit is contained in:
InsanusMokrassar 2022-06-03 17:52:58 +06:00
parent 8eed435302
commit bcb0e42fa2
6 changed files with 174 additions and 81 deletions

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

@ -43,3 +43,10 @@ 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

@ -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

@ -7,7 +7,7 @@ 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.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,11 +16,14 @@ 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(
@ -30,7 +33,9 @@ class KtorReadStandardCrudRepoClient<ObjectType, IdType> (
"id" to idSerializer(id) "id" 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(
@ -40,24 +45,30 @@ class KtorReadStandardCrudRepoClient<ObjectType, IdType> (
"id" to idSerializer(id) "id" 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 +76,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 +86,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> (
readDelegate: ReadStandardCRUDRepo<ObjectType, IdType>,
writeDelegate: WriteStandardCRUDRepo<ObjectType, IdType, InputValue>
) : StandardCRUDRepo<ObjectType, IdType, InputValue> by DelegateBasedStandardCRUDRepo(
readDelegate,
writeDelegate
) {
companion object {
inline operator fun <reified ObjectType, reified IdType, reified InputValue>invoke(
baseUrl: String, baseUrl: String,
httpClient: HttpClient, httpClient: HttpClient,
objectTypeInfo: TypeInfo, objectTypeInfo: TypeInfo,
idSerializer: suspend (IdType) -> String contentType: ContentType,
) : StandardCRUDRepo<ObjectType, IdType, InputValue>, noinline idSerializer: suspend (IdType) -> String
ReadStandardCRUDRepo<ObjectType, IdType> by KtorReadStandardCrudRepoClient( ) = KtorStandardCrudRepoClient(
KtorReadStandardCrudRepoClient(
baseUrl, baseUrl,
httpClient, httpClient,
objectTypeInfo, objectTypeInfo,
contentType,
idSerializer idSerializer
), ),
WriteStandardCRUDRepo<ObjectType, IdType, InputValue> by KtorWriteStandardCrudRepoClient( KtorWriteStandardCrudRepoClient<ObjectType, IdType, InputValue>(
baseUrl, baseUrl,
httpClient httpClient,
) { contentType
constructor( )
)
inline operator fun <reified ObjectType, reified IdType, reified InputValue>invoke(
baseUrl: String, baseUrl: String,
subpart: String, subpart: String,
httpClient: HttpClient, httpClient: HttpClient,
objectTypeInfo: TypeInfo, objectTypeInfo: TypeInfo,
idSerializer: suspend (IdType) -> String contentType: ContentType,
) : this( noinline idSerializer: suspend (IdType) -> String
buildStandardUrl(baseUrl, subpart), httpClient, objectTypeInfo, idSerializer ) = 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() }
)
} }
} }