add crud repo server and change a lot of different things
This commit is contained in:
parent
14d6915282
commit
b94417b77d
@ -4,7 +4,6 @@ import com.insanusmokrassar.postssystem.core.content.*
|
|||||||
import com.insanusmokrassar.postssystem.core.content.api.WriteContentRepo
|
import com.insanusmokrassar.postssystem.core.content.api.WriteContentRepo
|
||||||
import com.insanusmokrassar.postssystem.core.ktor.*
|
import com.insanusmokrassar.postssystem.core.ktor.*
|
||||||
import com.insanusmokrassar.postssystem.ktor.client.*
|
import com.insanusmokrassar.postssystem.ktor.client.*
|
||||||
import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.serialization.builtins.nullable
|
import kotlinx.serialization.builtins.nullable
|
||||||
@ -15,16 +14,14 @@ class WriteContentRepoKtorClient(
|
|||||||
private val client: HttpClient = HttpClient()
|
private val client: HttpClient = HttpClient()
|
||||||
) : WriteContentRepo {
|
) : WriteContentRepo {
|
||||||
override val contentCreatedFlow: Flow<RegisteredContent> = client.createStandardWebsocketFlow(
|
override val contentCreatedFlow: Flow<RegisteredContent> = client.createStandardWebsocketFlow(
|
||||||
"$baseUrl/$contentCreatedFlowRoute"
|
"$baseUrl/$contentCreatedFlowRoute",
|
||||||
) {
|
deserializer = RegisteredContent.serializer()
|
||||||
standardKtorSerialFormat.decodeFromByteArray(RegisteredContent.serializer(), it)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
override val contentDeletedFlow: Flow<RegisteredContent> = client.createStandardWebsocketFlow(
|
override val contentDeletedFlow: Flow<RegisteredContent> = client.createStandardWebsocketFlow(
|
||||||
"$baseUrl/$contentDeletedFlowRoute"
|
"$baseUrl/$contentDeletedFlowRoute",
|
||||||
) {
|
deserializer = RegisteredContent.serializer()
|
||||||
standardKtorSerialFormat.decodeFromByteArray(RegisteredContent.serializer(), it)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun registerContent(content: Content): RegisteredContent? = client.unipost(
|
override suspend fun registerContent(content: Content): RegisteredContent? = client.unipost(
|
||||||
"$baseUrl/$registerContentRoute",
|
"$baseUrl/$registerContentRoute",
|
||||||
|
@ -4,7 +4,6 @@ import com.insanusmokrassar.postssystem.core.ktor.*
|
|||||||
import com.insanusmokrassar.postssystem.core.post.*
|
import com.insanusmokrassar.postssystem.core.post.*
|
||||||
import com.insanusmokrassar.postssystem.core.post.repo.WritePostsRepo
|
import com.insanusmokrassar.postssystem.core.post.repo.WritePostsRepo
|
||||||
import com.insanusmokrassar.postssystem.ktor.client.*
|
import com.insanusmokrassar.postssystem.ktor.client.*
|
||||||
import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.websocket.WebSockets
|
import io.ktor.client.features.websocket.WebSockets
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@ -18,20 +17,17 @@ class WritePostsRepoKtorClient (
|
|||||||
}
|
}
|
||||||
) : WritePostsRepo {
|
) : WritePostsRepo {
|
||||||
override val postCreatedFlow: Flow<RegisteredPost> = client.createStandardWebsocketFlow(
|
override val postCreatedFlow: Flow<RegisteredPost> = client.createStandardWebsocketFlow(
|
||||||
"$baseUrl/$postCreatedFlowRoute"
|
"$baseUrl/$postCreatedFlowRoute",
|
||||||
) {
|
deserializer = RegisteredPost.serializer()
|
||||||
standardKtorSerialFormat.decodeFromByteArray(RegisteredPost.serializer(), it)
|
)
|
||||||
}
|
|
||||||
override val postDeletedFlow: Flow<RegisteredPost> = client.createStandardWebsocketFlow(
|
override val postDeletedFlow: Flow<RegisteredPost> = client.createStandardWebsocketFlow(
|
||||||
"$baseUrl/$postDeletedFlowRoute"
|
"$baseUrl/$postDeletedFlowRoute",
|
||||||
) {
|
deserializer = RegisteredPost.serializer()
|
||||||
standardKtorSerialFormat.decodeFromByteArray(RegisteredPost.serializer(), it)
|
)
|
||||||
}
|
|
||||||
override val postUpdatedFlow: Flow<RegisteredPost> = client.createStandardWebsocketFlow(
|
override val postUpdatedFlow: Flow<RegisteredPost> = client.createStandardWebsocketFlow(
|
||||||
"$baseUrl/$postUpdatedFlowRoute"
|
"$baseUrl/$postUpdatedFlowRoute",
|
||||||
) {
|
deserializer = RegisteredPost.serializer()
|
||||||
standardKtorSerialFormat.decodeFromByteArray(RegisteredPost.serializer(), it)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun createPost(post: Post): RegisteredPost? = client.unipost(
|
override suspend fun createPost(post: Post): RegisteredPost? = client.unipost(
|
||||||
"$baseUrl/$createPostRoute",
|
"$baseUrl/$createPostRoute",
|
||||||
|
@ -15,12 +15,8 @@ import kotlinx.serialization.builtins.serializer
|
|||||||
fun Route.configureWriteContentRepoRoutes(
|
fun Route.configureWriteContentRepoRoutes(
|
||||||
proxyTo: WriteContentRepo
|
proxyTo: WriteContentRepo
|
||||||
) {
|
) {
|
||||||
includeWebsocketHandling(contentCreatedFlowRoute, proxyTo.contentCreatedFlow) {
|
includeWebsocketHandling(contentCreatedFlowRoute, proxyTo.contentCreatedFlow, RegisteredContent.serializer())
|
||||||
standardKtorSerialFormat.encodeToByteArray(RegisteredContent.serializer(), it)
|
includeWebsocketHandling(contentDeletedFlowRoute, proxyTo.contentDeletedFlow, RegisteredContent.serializer())
|
||||||
}
|
|
||||||
includeWebsocketHandling(contentDeletedFlowRoute, proxyTo.contentDeletedFlow) {
|
|
||||||
standardKtorSerialFormat.encodeToByteArray(RegisteredContent.serializer(), it)
|
|
||||||
}
|
|
||||||
post(registerContentRoute) {
|
post(registerContentRoute) {
|
||||||
val content = call.uniload(Content.serializer())
|
val content = call.uniload(Content.serializer())
|
||||||
val registered = proxyTo.registerContent(content)
|
val registered = proxyTo.registerContent(content)
|
||||||
|
@ -15,15 +15,9 @@ import kotlinx.serialization.builtins.serializer
|
|||||||
fun Route.configureWritePostsRepoRoutes(
|
fun Route.configureWritePostsRepoRoutes(
|
||||||
proxyTo: WritePostsRepo
|
proxyTo: WritePostsRepo
|
||||||
) {
|
) {
|
||||||
includeWebsocketHandling(postCreatedFlowRoute, proxyTo.postCreatedFlow) {
|
includeWebsocketHandling(postCreatedFlowRoute, proxyTo.postCreatedFlow, RegisteredPost.serializer())
|
||||||
standardKtorSerialFormat.encodeToByteArray(RegisteredPost.serializer(), it)
|
includeWebsocketHandling(postDeletedFlowRoute, proxyTo.postDeletedFlow, RegisteredPost.serializer())
|
||||||
}
|
includeWebsocketHandling(postUpdatedFlowRoute, proxyTo.postUpdatedFlow, RegisteredPost.serializer())
|
||||||
includeWebsocketHandling(postDeletedFlowRoute, proxyTo.postDeletedFlow) {
|
|
||||||
standardKtorSerialFormat.encodeToByteArray(RegisteredPost.serializer(), it)
|
|
||||||
}
|
|
||||||
includeWebsocketHandling(postUpdatedFlowRoute, proxyTo.postUpdatedFlow) {
|
|
||||||
standardKtorSerialFormat.encodeToByteArray(RegisteredPost.serializer(), it)
|
|
||||||
}
|
|
||||||
|
|
||||||
post(createPostRoute) {
|
post(createPostRoute) {
|
||||||
call.unianswer(
|
call.unianswer(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.insanusmokrassar.postssystem.ktor.client
|
package com.insanusmokrassar.postssystem.ktor.client
|
||||||
|
|
||||||
import com.insanusmokrassar.postssystem.ktor.asCorrectWebSocketUrl
|
import com.insanusmokrassar.postssystem.ktor.asCorrectWebSocketUrl
|
||||||
|
import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
|
||||||
import com.insanusmokrassar.postssystem.utils.common.safely
|
import com.insanusmokrassar.postssystem.utils.common.safely
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.websocket.ws
|
import io.ktor.client.features.websocket.ws
|
||||||
@ -8,6 +9,7 @@ import io.ktor.http.cio.websocket.Frame
|
|||||||
import io.ktor.http.cio.websocket.readBytes
|
import io.ktor.http.cio.websocket.readBytes
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.channelFlow
|
import kotlinx.coroutines.flow.channelFlow
|
||||||
|
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
|
||||||
@ -63,3 +65,19 @@ inline fun <T> HttpClient.createStandardWebsocketFlow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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: (Throwable?) -> Boolean = { true },
|
||||||
|
deserializer: DeserializationStrategy<T>
|
||||||
|
) = createStandardWebsocketFlow(
|
||||||
|
url,
|
||||||
|
checkReconnection
|
||||||
|
) {
|
||||||
|
standardKtorSerialFormat.decodeFromByteArray(deserializer, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
package com.insanusmokrassar.postssystem.ktor.server
|
package com.insanusmokrassar.postssystem.ktor.server
|
||||||
|
|
||||||
import com.insanusmokrassar.postssystem.ktor.CorrectCloseException
|
import com.insanusmokrassar.postssystem.ktor.CorrectCloseException
|
||||||
|
import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
|
||||||
import com.insanusmokrassar.postssystem.utils.common.safely
|
import com.insanusmokrassar.postssystem.utils.common.safely
|
||||||
import io.ktor.http.cio.websocket.*
|
import io.ktor.http.cio.websocket.*
|
||||||
import io.ktor.routing.Route
|
import io.ktor.routing.Route
|
||||||
import io.ktor.websocket.webSocket
|
import io.ktor.websocket.webSocket
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.serialization.DeserializationStrategy
|
||||||
|
import kotlinx.serialization.SerializationStrategy
|
||||||
|
|
||||||
private suspend fun DefaultWebSocketSession.checkReceivedAndCloseIfExists() {
|
private suspend fun DefaultWebSocketSession.checkReceivedAndCloseIfExists() {
|
||||||
if (incoming.poll() != null) {
|
if (incoming.poll() != null) {
|
||||||
@ -29,3 +32,14 @@ fun <T> Route.includeWebsocketHandling(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> Route.includeWebsocketHandling(
|
||||||
|
suburl: String,
|
||||||
|
flow: Flow<T>,
|
||||||
|
serializer: SerializationStrategy<T>
|
||||||
|
) = includeWebsocketHandling(
|
||||||
|
suburl,
|
||||||
|
flow
|
||||||
|
) {
|
||||||
|
standardKtorSerialFormat.encodeToByteArray(serializer, it)
|
||||||
|
}
|
||||||
|
@ -30,6 +30,14 @@ suspend fun ApplicationCall.getParameterOrSendError(
|
|||||||
field: String
|
field: String
|
||||||
) = parameters[field].also {
|
) = parameters[field].also {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
respond(HttpStatusCode.BadRequest, "request must contains $field")
|
respond(HttpStatusCode.BadRequest, "Request must contains $field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun ApplicationCall.getQueryParameterOrSendError(
|
||||||
|
field: String
|
||||||
|
) = request.queryParameters[field].also {
|
||||||
|
if (it == null) {
|
||||||
|
respond(HttpStatusCode.BadRequest, "Request query parametersmust contains $field")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,7 @@ class WebsocketsTest {
|
|||||||
val server = createKtorServer(host = "127.0.0.1", port = port) {
|
val server = createKtorServer(host = "127.0.0.1", port = port) {
|
||||||
install(WebSockets)
|
install(WebSockets)
|
||||||
routing {
|
routing {
|
||||||
includeWebsocketHandling(suburl, dataFlow) {
|
includeWebsocketHandling(suburl, dataFlow, Int.serializer())
|
||||||
standardKtorSerialFormat.encodeToByteArray(Int.serializer(), it)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.also {
|
}.also {
|
||||||
it.start(false)
|
it.start(false)
|
||||||
@ -42,10 +40,9 @@ class WebsocketsTest {
|
|||||||
}
|
}
|
||||||
val incomingWebsocketFlow = client.createStandardWebsocketFlow(
|
val incomingWebsocketFlow = client.createStandardWebsocketFlow(
|
||||||
"$serverUrl/$suburl",
|
"$serverUrl/$suburl",
|
||||||
{ false } // always skip reconnection
|
{ false }, // always skip reconnection
|
||||||
) {
|
deserializer = Int.serializer()
|
||||||
standardKtorSerialFormat.decodeFromByteArray(Int.serializer(), it)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
var currentlyCheckingData: Int? = null
|
var currentlyCheckingData: Int? = null
|
||||||
incomingWebsocketFlow.onEach {
|
incomingWebsocketFlow.onEach {
|
||||||
|
@ -6,6 +6,7 @@ String[] includes = [
|
|||||||
':utils:repos:common',
|
':utils:repos:common',
|
||||||
':utils:repos:ktor:common',
|
':utils:repos:ktor:common',
|
||||||
':utils:repos:ktor:client',
|
':utils:repos:ktor:client',
|
||||||
|
':utils:repos:ktor:server',
|
||||||
':utils:repos:exposed',
|
':utils:repos:exposed',
|
||||||
|
|
||||||
':exposed:commons',
|
':exposed:commons',
|
||||||
|
@ -21,11 +21,21 @@ interface WriteStandardCRUDRepo<ObjectType, IdType, InputValueType> : Repo {
|
|||||||
val updatedObjectsFlow: Flow<ObjectType>
|
val updatedObjectsFlow: Flow<ObjectType>
|
||||||
val deletedObjectsIdsFlow: Flow<IdType>
|
val deletedObjectsIdsFlow: Flow<IdType>
|
||||||
|
|
||||||
suspend fun create(vararg values: InputValueType): List<ObjectType>
|
suspend fun create(values: List<InputValueType>): List<ObjectType>
|
||||||
suspend fun update(id: IdType, value: InputValueType): ObjectType?
|
suspend fun update(id: IdType, value: InputValueType): ObjectType?
|
||||||
suspend fun update(vararg values: UpdatedValuePair<IdType, InputValueType>): List<ObjectType>
|
suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType>
|
||||||
suspend fun deleteById(vararg ids: IdType)
|
suspend fun deleteById(ids: List<IdType>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.create(
|
||||||
|
vararg values: InputValueType
|
||||||
|
): List<ObjectType> = create(values.toList())
|
||||||
|
suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.update(
|
||||||
|
vararg values: UpdatedValuePair<IdType, InputValueType>
|
||||||
|
): List<ObjectType> = update(values.toList())
|
||||||
|
suspend fun <ObjectType, IdType, InputValueType> WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>.deleteById(
|
||||||
|
vararg ids: IdType
|
||||||
|
) = deleteById(ids.toList())
|
||||||
|
|
||||||
interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>,
|
interface StandardCRUDRepo<ObjectType, IdType, InputValueType> : ReadStandardCRUDRepo<ObjectType, IdType>,
|
||||||
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
|
WriteStandardCRUDRepo<ObjectType, IdType, InputValueType>
|
||||||
|
@ -27,20 +27,20 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
|||||||
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asFlow()
|
override val deletedObjectsIdsFlow: Flow<IdType> = deleteObjectsIdsChannel.asFlow()
|
||||||
|
|
||||||
abstract val InsertStatement<Number>.asObject: ObjectType
|
abstract val InsertStatement<Number>.asObject: ObjectType
|
||||||
abstract val selectByIds: SqlExpressionBuilder.(Array<out IdType>) -> Op<Boolean>
|
abstract val selectByIds: SqlExpressionBuilder.(List<out IdType>) -> Op<Boolean>
|
||||||
|
|
||||||
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
|
protected abstract fun insert(value: InputValueType, it: InsertStatement<Number>)
|
||||||
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
|
protected abstract fun update(id: IdType, value: InputValueType, it: UpdateStatement)
|
||||||
|
|
||||||
protected open suspend fun onBeforeCreate(vararg value: InputValueType) {}
|
protected open suspend fun onBeforeCreate(value: List<InputValueType>) {}
|
||||||
private fun createWithoutNotification(value: InputValueType): ObjectType {
|
private fun createWithoutNotification(value: InputValueType): ObjectType {
|
||||||
return transaction(database) {
|
return transaction(database) {
|
||||||
insert { insert(value, it) }.asObject
|
insert { insert(value, it) }.asObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun create(vararg values: InputValueType): List<ObjectType> {
|
override suspend fun create(values: List<InputValueType>): List<ObjectType> {
|
||||||
onBeforeCreate(*values)
|
onBeforeCreate(values)
|
||||||
return transaction(db = database) {
|
return transaction(db = database) {
|
||||||
values.map { value -> createWithoutNotification(value) }
|
values.map { value -> createWithoutNotification(value) }
|
||||||
}.also {
|
}.also {
|
||||||
@ -50,7 +50,7 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open suspend fun onBeforeUpdate(vararg value: UpdatedValuePair<IdType, InputValueType>) {}
|
protected open suspend fun onBeforeUpdate(value: List<UpdatedValuePair<IdType, InputValueType>>) {}
|
||||||
private fun updateWithoutNotification(id: IdType, value: InputValueType): ObjectType? {
|
private fun updateWithoutNotification(id: IdType, value: InputValueType): ObjectType? {
|
||||||
return transaction(db = database) {
|
return transaction(db = database) {
|
||||||
update(
|
update(
|
||||||
@ -72,15 +72,15 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
|
override suspend fun update(id: IdType, value: InputValueType): ObjectType? {
|
||||||
onBeforeUpdate(id to value)
|
onBeforeUpdate(listOf(id to value))
|
||||||
return updateWithoutNotification(id, value).also {
|
return updateWithoutNotification(id, value).also {
|
||||||
if (it != null) {
|
if (it != null) {
|
||||||
updateObjectsChannel.send(it)
|
updateObjectsChannel.send(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override suspend fun update(vararg values: UpdatedValuePair<IdType, InputValueType>): List<ObjectType> {
|
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValueType>>): List<ObjectType> {
|
||||||
onBeforeUpdate(*values)
|
onBeforeUpdate(values)
|
||||||
return (
|
return (
|
||||||
transaction(db = database) {
|
transaction(db = database) {
|
||||||
values.map { (id, value) -> updateWithoutNotification(id, value) }
|
values.map { (id, value) -> updateWithoutNotification(id, value) }
|
||||||
@ -93,9 +93,9 @@ abstract class AbstractExposedWriteCRUDRepo<ObjectType, IdType, InputValueType>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected open suspend fun onBeforeDelete(vararg ids: IdType) {}
|
protected open suspend fun onBeforeDelete(ids: List<IdType>) {}
|
||||||
override suspend fun deleteById(vararg ids: IdType) {
|
override suspend fun deleteById(ids: List<IdType>) {
|
||||||
onBeforeDelete(*ids)
|
onBeforeDelete(ids)
|
||||||
transaction(db = database) {
|
transaction(db = database) {
|
||||||
deleteWhere(null, null) {
|
deleteWhere(null, null) {
|
||||||
selectByIds(ids)
|
selectByIds(ids)
|
||||||
|
@ -2,15 +2,12 @@ package com.insanusmokrassar.postssystem.utils.repos.ktor.client
|
|||||||
|
|
||||||
import com.insanusmokrassar.postssystem.ktor.*
|
import com.insanusmokrassar.postssystem.ktor.*
|
||||||
import com.insanusmokrassar.postssystem.ktor.client.*
|
import com.insanusmokrassar.postssystem.ktor.client.*
|
||||||
import com.insanusmokrassar.postssystem.utils.common.pagination.Pagination
|
|
||||||
import com.insanusmokrassar.postssystem.utils.common.pagination.PaginationResult
|
|
||||||
import com.insanusmokrassar.postssystem.utils.repos.*
|
import com.insanusmokrassar.postssystem.utils.repos.*
|
||||||
import com.insanusmokrassar.postssystem.utils.repos.ktor.common.*
|
import com.insanusmokrassar.postssystem.utils.repos.ktor.common.*
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.serialization.KSerializer
|
import kotlinx.serialization.KSerializer
|
||||||
import kotlinx.serialization.builtins.*
|
import kotlinx.serialization.builtins.*
|
||||||
import kotlinx.serialization.encodeToHexString
|
|
||||||
|
|
||||||
class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
|
class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
|
||||||
private val baseUrl: String,
|
private val baseUrl: String,
|
||||||
@ -23,45 +20,47 @@ class KtorWriteStandardCrudRepo<ObjectType, IdType, InputValue> (
|
|||||||
private val listObjectsSerializer = ListSerializer(objectsSerializer)
|
private val listObjectsSerializer = ListSerializer(objectsSerializer)
|
||||||
private val listInputSerializer = ListSerializer(inputsSerializer)
|
private val listInputSerializer = ListSerializer(inputsSerializer)
|
||||||
private val listIdsSerializer = ListSerializer(idsSerializer)
|
private val listIdsSerializer = ListSerializer(idsSerializer)
|
||||||
private val inputUpdateSerializer = TemporalInputObjectForUpdate.serializer(
|
private val inputUpdateSerializer = PairSerializer(
|
||||||
idsSerializer,
|
idsSerializer,
|
||||||
inputsSerializer
|
inputsSerializer
|
||||||
)
|
)
|
||||||
private val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer)
|
private val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer)
|
||||||
|
|
||||||
override val newObjectsFlow: Flow<ObjectType> = client.createStandardWebsocketFlow(
|
override val newObjectsFlow: Flow<ObjectType> = client.createStandardWebsocketFlow(
|
||||||
buildStandardUrl(baseUrl, newObjectsFlowRouting)
|
buildStandardUrl(baseUrl, newObjectsFlowRouting),
|
||||||
) { standardKtorSerialFormat.decodeFromByteArray(objectsSerializer, it) }
|
deserializer = objectsSerializer
|
||||||
|
)
|
||||||
override val updatedObjectsFlow: Flow<ObjectType> = client.createStandardWebsocketFlow(
|
override val updatedObjectsFlow: Flow<ObjectType> = client.createStandardWebsocketFlow(
|
||||||
buildStandardUrl(baseUrl, updatedObjectsFlowRouting)
|
buildStandardUrl(baseUrl, updatedObjectsFlowRouting),
|
||||||
) { standardKtorSerialFormat.decodeFromByteArray(objectsSerializer, it) }
|
deserializer = objectsSerializer
|
||||||
|
)
|
||||||
override val deletedObjectsIdsFlow: Flow<IdType> = client.createStandardWebsocketFlow(
|
override val deletedObjectsIdsFlow: Flow<IdType> = client.createStandardWebsocketFlow(
|
||||||
buildStandardUrl(baseUrl, deletedObjectsIdsFlowRouting)
|
buildStandardUrl(baseUrl, deletedObjectsIdsFlowRouting),
|
||||||
) { standardKtorSerialFormat.decodeFromByteArray(idsSerializer, it) }
|
deserializer = idsSerializer
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
override suspend fun create(values: List<InputValue>): List<ObjectType> = client.unipost(
|
||||||
override suspend fun create(vararg values: InputValue): List<ObjectType> = client.unipost(
|
|
||||||
buildStandardUrl(baseUrl, createRouting),
|
buildStandardUrl(baseUrl, createRouting),
|
||||||
BodyPair(listInputSerializer, values.toList()),
|
BodyPair(listInputSerializer, values),
|
||||||
listObjectsSerializer
|
listObjectsSerializer
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun update(id: IdType, value: InputValue): ObjectType? = client.unipost(
|
override suspend fun update(id: IdType, value: InputValue): ObjectType? = client.unipost(
|
||||||
buildStandardUrl(baseUrl, updateRouting),
|
buildStandardUrl(baseUrl, updateRouting),
|
||||||
BodyPair(inputUpdateSerializer, TemporalInputObjectForUpdate(id, value)),
|
BodyPair(inputUpdateSerializer, id to value),
|
||||||
objectsNullableSerializer
|
objectsNullableSerializer
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun update(vararg values: UpdatedValuePair<IdType, InputValue>): List<ObjectType> = client.unipost(
|
override suspend fun update(values: List<UpdatedValuePair<IdType, InputValue>>): List<ObjectType> = client.unipost(
|
||||||
buildStandardUrl(baseUrl, updateManyRouting),
|
buildStandardUrl(baseUrl, updateManyRouting),
|
||||||
BodyPair(listInputUpdateSerializer, values.map { TemporalInputObjectForUpdate(it.first, it.second) }),
|
BodyPair(listInputUpdateSerializer, values),
|
||||||
listObjectsSerializer
|
listObjectsSerializer
|
||||||
)
|
)
|
||||||
|
|
||||||
override suspend fun deleteById(vararg ids: IdType) = client.unipost(
|
override suspend fun deleteById(ids: List<IdType>) = client.unipost(
|
||||||
buildStandardUrl(baseUrl, deleteByIdRouting),
|
buildStandardUrl(baseUrl, deleteByIdRouting),
|
||||||
BodyPair(listIdsSerializer, ids.toList()),
|
BodyPair(listIdsSerializer, ids),
|
||||||
Unit.serializer()
|
Unit.serializer()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
64
utils/repos/ktor/server/build.gradle
Normal file
64
utils/repos/ktor/server/build.gradle
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
|
classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:$gradle_bintray_plugin_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform" version "$kotlin_version"
|
||||||
|
id "org.jetbrains.kotlin.plugin.serialization" version "$kotlin_version"
|
||||||
|
}
|
||||||
|
|
||||||
|
project.version = "$core_version"
|
||||||
|
project.group = "com.insanusmokrassar"
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
jcenter()
|
||||||
|
mavenCentral()
|
||||||
|
maven { url "https://kotlin.bintray.com/kotlinx" }
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
jvm()
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('stdlib')
|
||||||
|
api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||||
|
|
||||||
|
api projectByName("postssystem.utils.repos.ktor.common")
|
||||||
|
api projectByName("postssystem.ktor.server")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commonTest {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('test-common')
|
||||||
|
implementation kotlin('test-annotations-common')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jvmMain {
|
||||||
|
dependencies {
|
||||||
|
api "io.ktor:ktor-server:$ktor_version"
|
||||||
|
api "io.ktor:ktor-server-host-common:$ktor_version"
|
||||||
|
api "io.ktor:ktor-server-netty:$ktor_version"
|
||||||
|
api "io.ktor:ktor-websockets:$ktor_version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jvmTest {
|
||||||
|
dependencies {
|
||||||
|
implementation kotlin('test-junit')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.insanusmokrassar.postssystem.utils.repos.ktor.server
|
||||||
|
|
||||||
|
import com.insanusmokrassar.postssystem.ktor.server.*
|
||||||
|
import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
|
||||||
|
import com.insanusmokrassar.postssystem.utils.common.pagination.PaginationResult
|
||||||
|
import com.insanusmokrassar.postssystem.utils.repos.ReadStandardCRUDRepo
|
||||||
|
import com.insanusmokrassar.postssystem.utils.repos.ktor.common.*
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.routing.Route
|
||||||
|
import io.ktor.routing.get
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.builtins.serializer
|
||||||
|
import kotlinx.serialization.decodeFromHexString
|
||||||
|
|
||||||
|
fun <ObjectType, IdType> Route.configureReadStandardCrudRepoRoutes(
|
||||||
|
originalRepo: ReadStandardCRUDRepo<ObjectType, IdType>,
|
||||||
|
objectsSerializer: KSerializer<ObjectType>,
|
||||||
|
objectsNullableSerializer: KSerializer<ObjectType?>,
|
||||||
|
idsSerializer: KSerializer<IdType>
|
||||||
|
) {
|
||||||
|
val paginationResultSerializer = PaginationResult.serializer(objectsSerializer)
|
||||||
|
|
||||||
|
get(getByPaginationRouting) {
|
||||||
|
val pagination = call.request.queryParameters.extractPagination
|
||||||
|
|
||||||
|
call.unianswer(
|
||||||
|
paginationResultSerializer,
|
||||||
|
originalRepo.getByPagination(pagination)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
get(getByIdRouting) {
|
||||||
|
val id = standardKtorSerialFormat.decodeFromHexString(
|
||||||
|
idsSerializer,
|
||||||
|
call.getQueryParameterOrSendError("id") ?: return@get
|
||||||
|
)
|
||||||
|
|
||||||
|
call.unianswer(
|
||||||
|
objectsNullableSerializer,
|
||||||
|
originalRepo.getById(id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
get(containsRouting) {
|
||||||
|
val id = standardKtorSerialFormat.decodeFromHexString(
|
||||||
|
idsSerializer,
|
||||||
|
call.getQueryParameterOrSendError("id") ?: return@get
|
||||||
|
)
|
||||||
|
|
||||||
|
call.unianswer(
|
||||||
|
Boolean.serializer(),
|
||||||
|
originalRepo.contains(id)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package com.insanusmokrassar.postssystem.utils.repos.ktor.server
|
||||||
|
|
||||||
|
import com.insanusmokrassar.postssystem.ktor.server.*
|
||||||
|
import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
|
||||||
|
import com.insanusmokrassar.postssystem.utils.common.pagination.PaginationResult
|
||||||
|
import com.insanusmokrassar.postssystem.utils.repos.*
|
||||||
|
import com.insanusmokrassar.postssystem.utils.repos.ktor.common.*
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.routing.*
|
||||||
|
import io.ktor.routing.get
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.builtins.*
|
||||||
|
import kotlinx.serialization.decodeFromHexString
|
||||||
|
|
||||||
|
fun <ObjectType, IdType, InputValue> Route.configureStandardCrudRepoRoutes(
|
||||||
|
baseSubpart: String,
|
||||||
|
originalRepo: StandardCRUDRepo<ObjectType, IdType, InputValue>,
|
||||||
|
objectsSerializer: KSerializer<ObjectType>,
|
||||||
|
objectsNullableSerializer: KSerializer<ObjectType?>,
|
||||||
|
inputsSerializer: KSerializer<InputValue>,
|
||||||
|
idsSerializer: KSerializer<IdType>
|
||||||
|
) {
|
||||||
|
route(baseSubpart) {
|
||||||
|
configureReadStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, idsSerializer)
|
||||||
|
configureWriteStandardCrudRepoRoutes(originalRepo, objectsSerializer, objectsNullableSerializer, inputsSerializer, idsSerializer)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
package com.insanusmokrassar.postssystem.utils.repos.ktor.server
|
||||||
|
|
||||||
|
import com.insanusmokrassar.postssystem.ktor.server.*
|
||||||
|
import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
|
||||||
|
import com.insanusmokrassar.postssystem.utils.common.pagination.PaginationResult
|
||||||
|
import com.insanusmokrassar.postssystem.utils.repos.ReadStandardCRUDRepo
|
||||||
|
import com.insanusmokrassar.postssystem.utils.repos.WriteStandardCRUDRepo
|
||||||
|
import com.insanusmokrassar.postssystem.utils.repos.ktor.common.*
|
||||||
|
import io.ktor.application.call
|
||||||
|
import io.ktor.routing.*
|
||||||
|
import io.ktor.routing.get
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import kotlinx.serialization.builtins.*
|
||||||
|
import kotlinx.serialization.decodeFromHexString
|
||||||
|
|
||||||
|
fun <ObjectType, IdType, InputValue> Route.configureWriteStandardCrudRepoRoutes(
|
||||||
|
originalRepo: WriteStandardCRUDRepo<ObjectType, IdType, InputValue>,
|
||||||
|
objectsSerializer: KSerializer<ObjectType>,
|
||||||
|
objectsNullableSerializer: KSerializer<ObjectType?>,
|
||||||
|
inputsSerializer: KSerializer<InputValue>,
|
||||||
|
idsSerializer: KSerializer<IdType>
|
||||||
|
) {
|
||||||
|
val listObjectsSerializer = ListSerializer(objectsSerializer)
|
||||||
|
val listInputSerializer = ListSerializer(inputsSerializer)
|
||||||
|
val listIdsSerializer = ListSerializer(idsSerializer)
|
||||||
|
val inputUpdateSerializer = PairSerializer(
|
||||||
|
idsSerializer,
|
||||||
|
inputsSerializer
|
||||||
|
)
|
||||||
|
val listInputUpdateSerializer = ListSerializer(inputUpdateSerializer)
|
||||||
|
|
||||||
|
includeWebsocketHandling(
|
||||||
|
newObjectsFlowRouting,
|
||||||
|
originalRepo.newObjectsFlow,
|
||||||
|
objectsSerializer
|
||||||
|
)
|
||||||
|
includeWebsocketHandling(
|
||||||
|
updatedObjectsFlowRouting,
|
||||||
|
originalRepo.updatedObjectsFlow,
|
||||||
|
objectsSerializer
|
||||||
|
)
|
||||||
|
includeWebsocketHandling(
|
||||||
|
deletedObjectsIdsFlowRouting,
|
||||||
|
originalRepo.deletedObjectsIdsFlow,
|
||||||
|
idsSerializer
|
||||||
|
)
|
||||||
|
|
||||||
|
post(createRouting) {
|
||||||
|
call.unianswer(
|
||||||
|
listObjectsSerializer,
|
||||||
|
originalRepo.create(
|
||||||
|
call.uniload(listInputSerializer)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
post(updateRouting) {
|
||||||
|
val (id, input) = call.uniload(inputUpdateSerializer)
|
||||||
|
call.unianswer(
|
||||||
|
objectsNullableSerializer,
|
||||||
|
originalRepo.update(
|
||||||
|
id, input
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
post(updateManyRouting) {
|
||||||
|
val updates = call.uniload(listInputUpdateSerializer)
|
||||||
|
call.unianswer(
|
||||||
|
listObjectsSerializer,
|
||||||
|
originalRepo.update(
|
||||||
|
updates
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
post(deleteByIdRouting) {
|
||||||
|
val ids = call.uniload(listIdsSerializer)
|
||||||
|
call.unianswer(
|
||||||
|
Unit.serializer(),
|
||||||
|
originalRepo.deleteById(
|
||||||
|
ids
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user