From 93bd094823cb1808eb35ecd6a71f518031b6c158 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Fri, 14 Aug 2020 12:06:37 +0600
Subject: [PATCH 1/3] add server realization of post repos

---
 .../core/content/api/ReadContentRepo.kt       |  2 +-
 .../core/exposed/ExposedContentRepo.kt        |  2 +-
 .../client/content/ContentRepoKtorClient.kt   | 33 ++++++----
 .../content/ReadContentRepoKtorClient.kt      |  2 +-
 .../postssystem/core/ktor/ContentRoutes.kt    |  2 +
 .../content/ContentRepoRoutingConfigurator.kt | 26 ++++++++
 .../ReadContentRepoRoutingConfigurator.kt     | 55 ++++++++++++++++
 .../WriteContentRepoRoutingConfigurator.kt    | 63 +++++++++++++++++++
 .../ktor/server/PaginationInUrl.kt            | 10 +++
 .../ApplicationRoutingConfigurator.kt         |  3 +-
 .../standardKtorSerialFormatContentType.kt    |  5 ++
 11 files changed, 187 insertions(+), 16 deletions(-)
 create mode 100644 core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ContentRepoRoutingConfigurator.kt
 create mode 100644 core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ReadContentRepoRoutingConfigurator.kt
 create mode 100644 core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/WriteContentRepoRoutingConfigurator.kt
 create mode 100644 ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/PaginationInUrl.kt
 create mode 100644 ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/standardKtorSerialFormatContentType.kt

diff --git a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentRepo.kt b/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentRepo.kt
index d17e3817..cbca2611 100644
--- a/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentRepo.kt
+++ b/core/api/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/content/api/ReadContentRepo.kt
@@ -21,5 +21,5 @@ interface ReadContentRepo {
     /**
      * @return all [RegisteredContent] by pages basing on their creation date
      */
-    suspend fun getContentByPagination(pagination: Pagination): PaginationResult<out RegisteredContent>
+    suspend fun getContentByPagination(pagination: Pagination): PaginationResult<RegisteredContent>
 }
\ No newline at end of file
diff --git a/core/exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentRepo.kt b/core/exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentRepo.kt
index e941308a..343093b0 100644
--- a/core/exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentRepo.kt
+++ b/core/exposed/src/main/kotlin/com/insanusmokrassar/postssystem/core/exposed/ExposedContentRepo.kt
@@ -131,7 +131,7 @@ private class ContentRepoDatabaseTable(
             select { idColumn.eq(id) }.limit(1).firstOrNull() ?.asRegisteredContent(content)
         }
     }
-    override suspend fun getContentByPagination(pagination: Pagination): PaginationResult<out RegisteredContent> {
+    override suspend fun getContentByPagination(pagination: Pagination): PaginationResult<RegisteredContent> {
         return transaction(
             db = database
         ) {
diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ContentRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ContentRepoKtorClient.kt
index b644855b..f1498a92 100644
--- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ContentRepoKtorClient.kt
+++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ContentRepoKtorClient.kt
@@ -1,18 +1,27 @@
 package com.insanusmokrassar.postssystem.core.ktor.client.content
 
 import com.insanusmokrassar.postssystem.core.content.api.*
+import com.insanusmokrassar.postssystem.core.ktor.contentRootRoute
 import io.ktor.client.HttpClient
 import io.ktor.client.features.websocket.WebSockets
 
-class ContentRepoKtorClient(
-    baseUrl: String,
-    client: HttpClient = HttpClient {
-        install(WebSockets)
-    }
-) : ContentRepo, ReadContentRepo by ReadContentRepoKtorClient(
-    baseUrl,
-    client
-), WriteContentRepo by WriteContentRepoKtorClient(
-    baseUrl,
-    client
-)
+class ContentRepoKtorClient private constructor(
+    readContentRepo: ReadContentRepo,
+    writeContentRepo: WriteContentRepo
+) : ContentRepo, ReadContentRepo by readContentRepo, WriteContentRepo by writeContentRepo {
+    constructor(
+        baseUrl: String,
+        client: HttpClient = HttpClient {
+            install(WebSockets)
+        }
+    ) : this(
+        ReadContentRepoKtorClient(
+            "$baseUrl/$contentRootRoute",
+            client
+        ),
+        WriteContentRepoKtorClient(
+            "$baseUrl/$contentRootRoute",
+            client
+        )
+    )
+}
diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt
index e9250fc4..568824bd 100644
--- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt
+++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt
@@ -27,7 +27,7 @@ class ReadContentRepoKtorClient(
         standardKtorSerialFormat.load(RegisteredContent.serializer().nullable, it)
     }
 
-    override suspend fun getContentByPagination(pagination: Pagination): PaginationResult<out RegisteredContent> = client.get<ByteArray>(
+    override suspend fun getContentByPagination(pagination: Pagination): PaginationResult<RegisteredContent> = client.get<ByteArray>(
         "$baseUrl/$getContentByPaginationRoute".includeQueryParams(
             pagination.asUrlQueryParts
         )
diff --git a/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/ContentRoutes.kt b/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/ContentRoutes.kt
index f5ba141d..84c7452e 100644
--- a/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/ContentRoutes.kt
+++ b/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/ContentRoutes.kt
@@ -1,5 +1,7 @@
 package com.insanusmokrassar.postssystem.core.ktor
 
+const val contentRootRoute = "content"
+
 const val getContentsIdsRoute = "getContentsIds"
 const val getContentByIdRoute = "getContentById"
 const val getContentByPaginationRoute = "getContentByPagination"
diff --git a/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ContentRepoRoutingConfigurator.kt b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ContentRepoRoutingConfigurator.kt
new file mode 100644
index 00000000..833cb6c0
--- /dev/null
+++ b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ContentRepoRoutingConfigurator.kt
@@ -0,0 +1,26 @@
+package com.insanusmokrassar.postssystem.core.ktor.server.content
+
+import com.insanusmokrassar.postssystem.core.content.api.ContentRepo
+import com.insanusmokrassar.postssystem.core.ktor.contentRootRoute
+import com.insanusmokrassar.postssystem.ktor.server.configurators.ApplicationRoutingConfigurator
+import io.ktor.routing.Route
+import io.ktor.routing.route
+
+fun Route.configureContentRepoRoutes(
+    proxyTo: ContentRepo
+) {
+    route(
+        contentRootRoute
+    ) {
+        configureReadContentRepoRoutes(proxyTo)
+        configureWriteContentRepoRoutes(proxyTo)
+    }
+}
+
+class ContentRepoRoutingConfigurator(
+    private val proxyTo: ContentRepo
+) : ApplicationRoutingConfigurator.Element {
+    override fun Route.invoke() {
+        configureContentRepoRoutes(proxyTo)
+    }
+}
\ No newline at end of file
diff --git a/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ReadContentRepoRoutingConfigurator.kt b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ReadContentRepoRoutingConfigurator.kt
new file mode 100644
index 00000000..c46205de
--- /dev/null
+++ b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ReadContentRepoRoutingConfigurator.kt
@@ -0,0 +1,55 @@
+package com.insanusmokrassar.postssystem.core.ktor.server.content
+
+import com.insanusmokrassar.postssystem.core.content.RegisteredContent
+import com.insanusmokrassar.postssystem.core.content.api.ReadContentRepo
+import com.insanusmokrassar.postssystem.core.ktor.*
+import com.insanusmokrassar.postssystem.ktor.server.configurators.ApplicationRoutingConfigurator
+import com.insanusmokrassar.postssystem.ktor.server.extractPagination
+import com.insanusmokrassar.postssystem.ktor.server.standardKtorSerialFormatContentType
+import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
+import io.ktor.application.call
+import io.ktor.http.HttpStatusCode
+import io.ktor.response.respond
+import io.ktor.response.respondBytes
+import io.ktor.routing.*
+import kotlinx.serialization.builtins.nullable
+
+fun Route.configureReadContentRepoRoutes(
+    proxyTo: ReadContentRepo
+) {
+    get(getContentsIdsRoute) {
+        call.respondBytes(
+            standardKtorSerialFormat.dump(contentIdsSerializer, proxyTo.getContentsIds()),
+            standardKtorSerialFormatContentType
+        )
+    }
+    get("$getContentByIdRoute/{id}") {
+        val id = call.parameters["id"].also {
+            if (it == null) call.respond(HttpStatusCode.BadRequest, "Request must contains id in route")
+        } ?: return@get
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                RegisteredContent.serializer().nullable,
+                proxyTo.getContentById(id)
+            ),
+            standardKtorSerialFormatContentType
+        )
+    }
+    get(getContentByPaginationRoute) {
+        val pagination = call.request.queryParameters.extractPagination
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                registeredContentPaginationResultSerializer,
+                proxyTo.getContentByPagination(pagination)
+            )
+        )
+    }
+}
+
+class ReadContentRepoRoutingConfigurator(
+    private val proxyTo: ReadContentRepo
+) : ApplicationRoutingConfigurator.Element {
+    override fun Route.invoke() {
+        configureReadContentRepoRoutes(proxyTo)
+    }
+}
diff --git a/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/WriteContentRepoRoutingConfigurator.kt b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/WriteContentRepoRoutingConfigurator.kt
new file mode 100644
index 00000000..1fa2eb22
--- /dev/null
+++ b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/WriteContentRepoRoutingConfigurator.kt
@@ -0,0 +1,63 @@
+package com.insanusmokrassar.postssystem.core.ktor.server.content
+
+import com.insanusmokrassar.postssystem.core.content.*
+import com.insanusmokrassar.postssystem.core.content.api.WriteContentRepo
+import com.insanusmokrassar.postssystem.core.ktor.*
+import com.insanusmokrassar.postssystem.ktor.server.configurators.ApplicationRoutingConfigurator
+import com.insanusmokrassar.postssystem.ktor.server.includeWebsocketHandling
+import com.insanusmokrassar.postssystem.ktor.server.standardKtorSerialFormatContentType
+import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
+import io.ktor.application.call
+import io.ktor.response.respondBytes
+import io.ktor.routing.Route
+import io.ktor.routing.post
+import io.ktor.util.toByteArray
+import kotlinx.serialization.builtins.nullable
+import kotlinx.serialization.builtins.serializer
+
+fun Route.configureWriteContentRepoRoutes(
+    proxyTo: WriteContentRepo
+) {
+    includeWebsocketHandling(contentCreatedFlowRoute, proxyTo.contentCreatedFlow) {
+        standardKtorSerialFormat.dump(RegisteredContent.serializer(), it)
+    }
+    includeWebsocketHandling(contentDeletedFlowRoute, proxyTo.contentDeletedFlow) {
+        standardKtorSerialFormat.dump(RegisteredContent.serializer(), it)
+    }
+    post(registerContentRoute) {
+        val content = standardKtorSerialFormat.load(
+            Content.serializer(),
+            call.request.receiveChannel().toByteArray()
+        )
+        val registered = proxyTo.registerContent(content)
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                RegisteredContent.serializer().nullable,
+                registered
+            ),
+            standardKtorSerialFormatContentType
+        )
+    }
+    post(deleteContentRoute) {
+        val contentId = standardKtorSerialFormat.load(
+            ContentId.serializer(),
+            call.request.receiveChannel().toByteArray()
+        )
+        val isDeleted = proxyTo.deleteContent(contentId)
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                Boolean.serializer(),
+                isDeleted
+            ),
+            standardKtorSerialFormatContentType
+        )
+    }
+}
+
+class WriteContentRepoRoutingConfigurator(
+    private val proxyTo: WriteContentRepo
+) : ApplicationRoutingConfigurator.Element {
+    override fun Route.invoke() {
+        configureWriteContentRepoRoutes(proxyTo)
+    }
+}
diff --git a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/PaginationInUrl.kt b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/PaginationInUrl.kt
new file mode 100644
index 00000000..b30b5691
--- /dev/null
+++ b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/PaginationInUrl.kt
@@ -0,0 +1,10 @@
+package com.insanusmokrassar.postssystem.ktor.server
+
+import com.insanusmokrassar.postssystem.utils.common.pagination.*
+import io.ktor.http.Parameters
+
+val Parameters.extractPagination: Pagination
+    get() = SimplePagination(
+        get("page") ?.toIntOrNull() ?: 0,
+        get("size") ?.toIntOrNull() ?: defaultMediumPageSize
+    )
diff --git a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/configurators/ApplicationRoutingConfigurator.kt b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/configurators/ApplicationRoutingConfigurator.kt
index 0e56e3b3..8ec15a65 100644
--- a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/configurators/ApplicationRoutingConfigurator.kt
+++ b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/configurators/ApplicationRoutingConfigurator.kt
@@ -1,6 +1,7 @@
 package com.insanusmokrassar.postssystem.ktor.server.configurators
 
 import io.ktor.application.*
+import io.ktor.routing.Route
 import io.ktor.routing.Routing
 import kotlinx.serialization.ContextualSerialization
 import kotlinx.serialization.Serializable
@@ -9,7 +10,7 @@ import kotlinx.serialization.Serializable
 class ApplicationRoutingConfigurator(
     private val elements: List<@ContextualSerialization Element>
 ) : KtorApplicationConfigurator {
-    interface Element { operator fun Routing.invoke() }
+    interface Element { operator fun Route.invoke() }
 
     override fun Application.configure() {
         try {
diff --git a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/standardKtorSerialFormatContentType.kt b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/standardKtorSerialFormatContentType.kt
new file mode 100644
index 00000000..e7e0e422
--- /dev/null
+++ b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/standardKtorSerialFormatContentType.kt
@@ -0,0 +1,5 @@
+package com.insanusmokrassar.postssystem.ktor.server
+
+import io.ktor.http.ContentType
+
+val standardKtorSerialFormatContentType = ContentType.Application.Cbor

From 8c3a48db99148c29d2e2f68b9a700606913367f6 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Fri, 14 Aug 2020 13:17:27 +0600
Subject: [PATCH 2/3] complete client-server part for core api

---
 .../ktor/client/post/PostsRepoKtorClient.kt   | 33 ++++---
 .../client/post/WritePostsRepoKtorClient.kt   |  4 +-
 .../postssystem/core/ktor/PostRoutes.kt       |  2 +
 .../ReadContentRepoRoutingConfigurator.kt     |  3 +-
 .../post/PostsRepoRoutingConfigurator.kt      | 24 +++++
 .../post/ReadPostsRepoRoutingConfigurator.kt  | 93 +++++++++++++++++++
 .../post/WritePostsRepoRoutingConfigurator.kt | 79 ++++++++++++++++
 .../ktor/server/FromToDateTimeInUrl.kt        | 11 +++
 8 files changed, 234 insertions(+), 15 deletions(-)
 create mode 100644 core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/PostsRepoRoutingConfigurator.kt
 create mode 100644 core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/ReadPostsRepoRoutingConfigurator.kt
 create mode 100644 core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/WritePostsRepoRoutingConfigurator.kt
 create mode 100644 ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/FromToDateTimeInUrl.kt

diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/PostsRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/PostsRepoKtorClient.kt
index d60c795a..924607e9 100644
--- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/PostsRepoKtorClient.kt
+++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/PostsRepoKtorClient.kt
@@ -1,18 +1,27 @@
 package com.insanusmokrassar.postssystem.core.ktor.client.post
 
+import com.insanusmokrassar.postssystem.core.ktor.postsRootRoute
 import com.insanusmokrassar.postssystem.core.post.repo.*
 import io.ktor.client.HttpClient
 import io.ktor.client.features.websocket.WebSockets
 
-class PostsRepoKtorClient(
-    baseUrl: String,
-    client: HttpClient = HttpClient {
-        install(WebSockets)
-    }
-) : PostsRepo, ReadPostsRepo by ReadPostsRepoKtorClient(
-    baseUrl,
-    client
-), WritePostsRepo by WritePostsRepoKtorClient(
-    baseUrl,
-    client
-)
+class PostsRepoKtorClient private constructor(
+    readPostsRepo: ReadPostsRepo,
+    writePostsRepo: WritePostsRepo
+) : PostsRepo, ReadPostsRepo by readPostsRepo, WritePostsRepo by writePostsRepo {
+    constructor(
+        baseUrl: String,
+        client: HttpClient = HttpClient {
+            install(WebSockets)
+        }
+    ) : this(
+        ReadPostsRepoKtorClient(
+            "${baseUrl}/$postsRootRoute",
+            client
+        ),
+        WritePostsRepoKtorClient(
+            "${baseUrl}/$postsRootRoute",
+            client
+        )
+    )
+}
diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/WritePostsRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/WritePostsRepoKtorClient.kt
index 8e8f2dc6..b4d271ce 100644
--- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/WritePostsRepoKtorClient.kt
+++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/WritePostsRepoKtorClient.kt
@@ -43,14 +43,14 @@ class WritePostsRepoKtorClient (
     }
 
     override suspend fun deletePost(id: PostId): Boolean = client.post<ByteArray> {
-        url("$baseUrl/$createPostRoute")
+        url("$baseUrl/$deletePostRoute")
         body = standardKtorSerialFormat.dump(PostId.serializer(), id)
     }.let {
         standardKtorSerialFormat.load(Boolean.serializer(), it)
     }
 
     override suspend fun updatePostContent(postId: PostId, post: Post): Boolean = client.post<ByteArray> {
-        url("$baseUrl/$createPostRoute")
+        url("$baseUrl/$updatePostContentRoute")
         body = standardKtorSerialFormat.dump(UpdatePostObject.serializer(), UpdatePostObject(postId, post))
     }.let {
         standardKtorSerialFormat.load(Boolean.serializer(), it)
diff --git a/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/PostRoutes.kt b/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/PostRoutes.kt
index 69633e2e..2e3f6c64 100644
--- a/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/PostRoutes.kt
+++ b/core/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/PostRoutes.kt
@@ -1,5 +1,7 @@
 package com.insanusmokrassar.postssystem.core.ktor
 
+const val postsRootRoute = "post"
+
 const val getPostsIdsRoute = "getPostsIds"
 const val getPostByIdRoute = "getPostById"
 const val getPostsByContentRoute = "getPostsByContent"
diff --git a/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ReadContentRepoRoutingConfigurator.kt b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ReadContentRepoRoutingConfigurator.kt
index c46205de..01e5b467 100644
--- a/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ReadContentRepoRoutingConfigurator.kt
+++ b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/content/ReadContentRepoRoutingConfigurator.kt
@@ -1,5 +1,6 @@
 package com.insanusmokrassar.postssystem.core.ktor.server.content
 
+import com.insanusmokrassar.postssystem.core.content.ContentId
 import com.insanusmokrassar.postssystem.core.content.RegisteredContent
 import com.insanusmokrassar.postssystem.core.content.api.ReadContentRepo
 import com.insanusmokrassar.postssystem.core.ktor.*
@@ -24,7 +25,7 @@ fun Route.configureReadContentRepoRoutes(
         )
     }
     get("$getContentByIdRoute/{id}") {
-        val id = call.parameters["id"].also {
+        val id: ContentId = call.parameters["id"].also {
             if (it == null) call.respond(HttpStatusCode.BadRequest, "Request must contains id in route")
         } ?: return@get
         call.respondBytes(
diff --git a/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/PostsRepoRoutingConfigurator.kt b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/PostsRepoRoutingConfigurator.kt
new file mode 100644
index 00000000..beb217c8
--- /dev/null
+++ b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/PostsRepoRoutingConfigurator.kt
@@ -0,0 +1,24 @@
+package com.insanusmokrassar.postssystem.core.ktor.server.post
+
+import com.insanusmokrassar.postssystem.core.ktor.postsRootRoute
+import com.insanusmokrassar.postssystem.core.post.repo.PostsRepo
+import com.insanusmokrassar.postssystem.ktor.server.configurators.ApplicationRoutingConfigurator
+import io.ktor.routing.Route
+import io.ktor.routing.route
+
+fun Route.configurePostsRepoRoutes(
+    proxyTo: PostsRepo
+) {
+    route(postsRootRoute) {
+        configureReadPostsRepoRoutes(proxyTo)
+        configureWritePostsRepoRoutes(proxyTo)
+    }
+}
+
+class PostsRepoRoutingConfigurator(
+    private val proxyTo: PostsRepo
+) : ApplicationRoutingConfigurator.Element {
+    override fun Route.invoke() {
+        configurePostsRepoRoutes(proxyTo)
+    }
+}
diff --git a/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/ReadPostsRepoRoutingConfigurator.kt b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/ReadPostsRepoRoutingConfigurator.kt
new file mode 100644
index 00000000..8a382995
--- /dev/null
+++ b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/ReadPostsRepoRoutingConfigurator.kt
@@ -0,0 +1,93 @@
+package com.insanusmokrassar.postssystem.core.ktor.server.post
+
+import com.insanusmokrassar.postssystem.core.content.ContentId
+import com.insanusmokrassar.postssystem.core.ktor.*
+import com.insanusmokrassar.postssystem.core.post.PostId
+import com.insanusmokrassar.postssystem.core.post.RegisteredPost
+import com.insanusmokrassar.postssystem.core.post.repo.ReadPostsRepo
+import com.insanusmokrassar.postssystem.ktor.server.*
+import com.insanusmokrassar.postssystem.ktor.server.configurators.ApplicationRoutingConfigurator
+import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
+import com.insanusmokrassar.postssystem.utils.common.MAX_DATE
+import com.insanusmokrassar.postssystem.utils.common.MIN_DATE
+import io.ktor.application.call
+import io.ktor.http.HttpStatusCode
+import io.ktor.response.respond
+import io.ktor.response.respondBytes
+import io.ktor.routing.Route
+import io.ktor.routing.get
+import kotlinx.serialization.builtins.nullable
+
+fun Route.configureReadPostsRepoRoutes(
+    proxyTo: ReadPostsRepo
+) {
+    get(getPostsIdsRoute) {
+        call.respondBytes(
+            standardKtorSerialFormat.dump(postIdsSerializer, proxyTo.getPostsIds()),
+            standardKtorSerialFormatContentType
+        )
+    }
+    get("$getPostByIdRoute/{id}") {
+        val id: PostId = call.parameters["id"].also {
+            if (it == null) {
+                call.respond(HttpStatusCode.BadRequest, "request must contains id")
+            }
+        } ?: return@get
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                RegisteredPost.serializer().nullable,
+                proxyTo.getPostById(id)
+            ),
+            standardKtorSerialFormatContentType
+        )
+    }
+    get("$getPostsByContentRoute/{id}") {
+        val id: ContentId = call.parameters["id"].also {
+            if (it == null) {
+                call.respond(HttpStatusCode.BadRequest, "request must contains id")
+            }
+        } ?: return@get
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                registeredPostsListSerializer,
+                proxyTo.getPostsByContent(id)
+            ),
+            standardKtorSerialFormatContentType
+        )
+    }
+    get(getPostsByCreatingDatesRoute) {
+        val fromToDateTime = call.request.queryParameters.extractFromToDateTime
+        val pagination = call.request.queryParameters.extractPagination
+
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                registeredPostsPaginationResultSerializer,
+                proxyTo.getPostsByCreatingDates(
+                    fromToDateTime.first ?: MIN_DATE,
+                    fromToDateTime.second ?: MAX_DATE,
+                    pagination
+                )
+            ),
+            standardKtorSerialFormatContentType
+        )
+    }
+    get(getPostsByPaginationRoute) {
+        val pagination = call.request.queryParameters.extractPagination
+
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                registeredPostsPaginationResultSerializer,
+                proxyTo.getPostsByPagination(pagination)
+            ),
+            standardKtorSerialFormatContentType
+        )
+    }
+}
+
+class ReadPostsRepoRoutingConfigurator(
+    private val proxyTo: ReadPostsRepo
+) : ApplicationRoutingConfigurator.Element {
+    override fun Route.invoke() {
+        configureReadPostsRepoRoutes(proxyTo)
+    }
+}
diff --git a/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/WritePostsRepoRoutingConfigurator.kt b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/WritePostsRepoRoutingConfigurator.kt
new file mode 100644
index 00000000..b4617082
--- /dev/null
+++ b/core/ktor/server/src/main/kotlin/com/insanusmokrassar/postssystem/core/ktor/server/post/WritePostsRepoRoutingConfigurator.kt
@@ -0,0 +1,79 @@
+package com.insanusmokrassar.postssystem.core.ktor.server.post
+
+import com.insanusmokrassar.postssystem.core.ktor.*
+import com.insanusmokrassar.postssystem.core.post.*
+import com.insanusmokrassar.postssystem.core.post.repo.WritePostsRepo
+import com.insanusmokrassar.postssystem.ktor.server.configurators.ApplicationRoutingConfigurator
+import com.insanusmokrassar.postssystem.ktor.server.includeWebsocketHandling
+import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
+import io.ktor.application.call
+import io.ktor.response.respondBytes
+import io.ktor.routing.*
+import io.ktor.util.toByteArray
+import kotlinx.serialization.builtins.nullable
+import kotlinx.serialization.builtins.serializer
+
+fun Route.configureWritePostsRepoRoutes(
+    proxyTo: WritePostsRepo
+) {
+    includeWebsocketHandling(postCreatedFlowRoute, proxyTo.postCreatedFlow) {
+        standardKtorSerialFormat.dump(RegisteredPost.serializer(), it)
+    }
+    includeWebsocketHandling(postDeletedFlowRoute, proxyTo.postDeletedFlow) {
+        standardKtorSerialFormat.dump(RegisteredPost.serializer(), it)
+    }
+    includeWebsocketHandling(postUpdatedFlowRoute, proxyTo.postUpdatedFlow) {
+        standardKtorSerialFormat.dump(RegisteredPost.serializer(), it)
+    }
+
+    post(createPostRoute) {
+        val post = standardKtorSerialFormat.load(
+            Post.serializer(),
+            call.request.receiveChannel().toByteArray()
+        )
+
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                RegisteredPost.serializer().nullable,
+                proxyTo.createPost(post)
+            )
+        )
+    }
+
+    post(deletePostRoute) {
+        val postId = standardKtorSerialFormat.load(
+            PostId.serializer(),
+            call.request.receiveChannel().toByteArray()
+        )
+
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                Boolean.serializer(),
+                proxyTo.deletePost(postId)
+            )
+        )
+    }
+
+    post(updatePostContentRoute) {
+        val updatePostObject = standardKtorSerialFormat.load(
+            UpdatePostObject.serializer(),
+            call.request.receiveChannel().toByteArray()
+        )
+
+        call.respondBytes(
+            standardKtorSerialFormat.dump(
+                Boolean.serializer(),
+                proxyTo.updatePostContent(updatePostObject.postId, updatePostObject.post)
+            )
+        )
+    }
+}
+
+class WritePostsRepoRoutingConfigurator(
+    private val proxyTo: WritePostsRepo
+) : ApplicationRoutingConfigurator.Element {
+    override fun Route.invoke() {
+        configureWritePostsRepoRoutes(proxyTo)
+    }
+
+}
diff --git a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/FromToDateTimeInUrl.kt b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/FromToDateTimeInUrl.kt
new file mode 100644
index 00000000..d35a4a96
--- /dev/null
+++ b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/FromToDateTimeInUrl.kt
@@ -0,0 +1,11 @@
+package com.insanusmokrassar.postssystem.ktor.server
+
+import com.insanusmokrassar.postssystem.ktor.FromToDateTime
+import com.soywiz.klock.DateTime
+import io.ktor.http.Parameters
+
+val Parameters.extractFromToDateTime: FromToDateTime
+    get() = FromToDateTime(
+        get("from") ?.toDoubleOrNull() ?.let { DateTime(it) },
+        get("to") ?.toDoubleOrNull() ?.let { DateTime(it) }
+    )

From 6d6bed2eb075bb62ac74c3fd63b48e05173caead Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Fri, 14 Aug 2020 18:05:58 +0600
Subject: [PATCH 3/3] add unified way to make post and get requests from client

---
 .../content/ReadContentRepoKtorClient.kt      | 21 +++++------
 .../content/WriteContentRepoKtorClient.kt     | 24 ++++++------
 .../client/post/ReadPostsRepoKtorClient.kt    | 37 +++++++++----------
 .../client/post/WritePostsRepoKtorClient.kt   | 36 ++++++++----------
 .../ktor/client/StandardHttpClientGetPost.kt  | 28 ++++++++++++++
 5 files changed, 81 insertions(+), 65 deletions(-)
 create mode 100644 ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/client/StandardHttpClientGetPost.kt

diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt
index 568824bd..4ec4c212 100644
--- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt
+++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/ReadContentRepoKtorClient.kt
@@ -5,6 +5,7 @@ import com.insanusmokrassar.postssystem.core.content.RegisteredContent
 import com.insanusmokrassar.postssystem.core.content.api.ReadContentRepo
 import com.insanusmokrassar.postssystem.core.ktor.*
 import com.insanusmokrassar.postssystem.ktor.*
+import com.insanusmokrassar.postssystem.ktor.client.uniget
 import com.insanusmokrassar.postssystem.utils.common.pagination.Pagination
 import com.insanusmokrassar.postssystem.utils.common.pagination.PaginationResult
 import io.ktor.client.HttpClient
@@ -21,17 +22,13 @@ class ReadContentRepoKtorClient(
         standardKtorSerialFormat.load(contentIdsSerializer, it)
     }
 
-    override suspend fun getContentById(id: ContentId): RegisteredContent? = client.get<ByteArray>(
-        "$baseUrl/$getContentByIdRoute/$id"
-    ).let {
-        standardKtorSerialFormat.load(RegisteredContent.serializer().nullable, it)
-    }
+    override suspend fun getContentById(id: ContentId): RegisteredContent? = client.uniget(
+        "$baseUrl/$getContentByIdRoute/$id",
+        RegisteredContent.serializer().nullable
+    )
 
-    override suspend fun getContentByPagination(pagination: Pagination): PaginationResult<RegisteredContent> = client.get<ByteArray>(
-        "$baseUrl/$getContentByPaginationRoute".includeQueryParams(
-            pagination.asUrlQueryParts
-        )
-    ).let {
-        standardKtorSerialFormat.load(registeredContentPaginationResultSerializer, it)
-    }
+    override suspend fun getContentByPagination(pagination: Pagination): PaginationResult<RegisteredContent> = client.uniget(
+        "$baseUrl/$getContentByPaginationRoute".includeQueryParams(pagination.asUrlQueryParts),
+        registeredContentPaginationResultSerializer
+    )
 }
\ No newline at end of file
diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/WriteContentRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/WriteContentRepoKtorClient.kt
index e7233e21..04126f49 100644
--- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/WriteContentRepoKtorClient.kt
+++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/content/WriteContentRepoKtorClient.kt
@@ -3,7 +3,7 @@ package com.insanusmokrassar.postssystem.core.ktor.client.content
 import com.insanusmokrassar.postssystem.core.content.*
 import com.insanusmokrassar.postssystem.core.content.api.WriteContentRepo
 import com.insanusmokrassar.postssystem.core.ktor.*
-import com.insanusmokrassar.postssystem.ktor.client.createStandardWebsocketFlow
+import com.insanusmokrassar.postssystem.ktor.client.*
 import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
 import io.ktor.client.HttpClient
 import io.ktor.client.request.post
@@ -28,17 +28,15 @@ class WriteContentRepoKtorClient(
         standardKtorSerialFormat.load(RegisteredContent.serializer(), it)
     }
 
-    override suspend fun registerContent(content: Content): RegisteredContent? = client.post<ByteArray> {
-        url("$baseUrl/$registerContentRoute")
-        body = standardKtorSerialFormat.dump(Content.serializer(), content)
-    }.let {
-        standardKtorSerialFormat.load(RegisteredContent.serializer().nullable, it)
-    }
+    override suspend fun registerContent(content: Content): RegisteredContent? = client.unipost(
+        "$baseUrl/$registerContentRoute",
+        BodyPair(Content.serializer(), content),
+        RegisteredContent.serializer().nullable
+    )
 
-    override suspend fun deleteContent(id: ContentId): Boolean = client.post<ByteArray> {
-        url("$baseUrl/$deleteContentRoute")
-        body = standardKtorSerialFormat.dump(ContentId.serializer(), id)
-    }.let {
-        standardKtorSerialFormat.load(Boolean.serializer(), it)
-    }
+    override suspend fun deleteContent(id: ContentId): Boolean = client.unipost(
+        "$baseUrl/$deleteContentRoute",
+        BodyPair(ContentId.serializer(), id),
+        Boolean.serializer()
+    )
 }
\ No newline at end of file
diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/ReadPostsRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/ReadPostsRepoKtorClient.kt
index d5d9559f..768253f0 100644
--- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/ReadPostsRepoKtorClient.kt
+++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/ReadPostsRepoKtorClient.kt
@@ -6,6 +6,7 @@ import com.insanusmokrassar.postssystem.core.post.PostId
 import com.insanusmokrassar.postssystem.core.post.RegisteredPost
 import com.insanusmokrassar.postssystem.core.post.repo.ReadPostsRepo
 import com.insanusmokrassar.postssystem.ktor.*
+import com.insanusmokrassar.postssystem.ktor.client.uniget
 import com.insanusmokrassar.postssystem.utils.common.pagination.Pagination
 import com.insanusmokrassar.postssystem.utils.common.pagination.PaginationResult
 import com.soywiz.klock.DateTime
@@ -23,35 +24,31 @@ class ReadPostsRepoKtorClient(
         standardKtorSerialFormat.load(postIdsSerializer, it)
     }
 
-    override suspend fun getPostById(id: PostId): RegisteredPost? = client.get<ByteArray>(
-        "$baseUrl/$getPostByIdRoute/$id"
-    ).let {
-        standardKtorSerialFormat.load(RegisteredPost.serializer().nullable, it)
-    }
+    override suspend fun getPostById(id: PostId): RegisteredPost? = client.uniget(
+        "$baseUrl/$getPostByIdRoute/$id",
+        RegisteredPost.serializer().nullable
+    )
 
-    override suspend fun getPostsByContent(id: ContentId): List<RegisteredPost> = client.get<ByteArray>(
-        "$baseUrl/$getPostsByContentRoute/$id"
-    ).let {
-        standardKtorSerialFormat.load(registeredPostsListSerializer, it)
-    }
+    override suspend fun getPostsByContent(id: ContentId): List<RegisteredPost> = client.uniget(
+        "$baseUrl/$getPostsByContentRoute/$id",
+        registeredPostsListSerializer
+    )
 
     override suspend fun getPostsByCreatingDates(
         from: DateTime,
         to: DateTime,
         pagination: Pagination
-    ): PaginationResult<RegisteredPost> = client.get<ByteArray>(
+    ): PaginationResult<RegisteredPost> = client.uniget(
         "$baseUrl/$getPostsByCreatingDatesRoute".includeQueryParams(
             (from to to).asFromToUrlPart + pagination.asUrlQueryParts
-        )
-    ).let {
-        standardKtorSerialFormat.load(registeredPostsPaginationResultSerializer, it)
-    }
+        ),
+        registeredPostsPaginationResultSerializer
+    )
 
-    override suspend fun getPostsByPagination(pagination: Pagination): PaginationResult<RegisteredPost> = client.get<ByteArray>(
+    override suspend fun getPostsByPagination(pagination: Pagination): PaginationResult<RegisteredPost> = client.uniget(
         "$baseUrl/$getPostsByPaginationRoute".includeQueryParams(
             pagination.asUrlQueryParts
-        )
-    ).let {
-        standardKtorSerialFormat.load(registeredPostsPaginationResultSerializer, it)
-    }
+        ),
+        registeredPostsPaginationResultSerializer
+    )
 }
\ No newline at end of file
diff --git a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/WritePostsRepoKtorClient.kt b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/WritePostsRepoKtorClient.kt
index b4d271ce..ec4c283d 100644
--- a/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/WritePostsRepoKtorClient.kt
+++ b/core/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/core/ktor/client/post/WritePostsRepoKtorClient.kt
@@ -3,7 +3,7 @@ package com.insanusmokrassar.postssystem.core.ktor.client.post
 import com.insanusmokrassar.postssystem.core.ktor.*
 import com.insanusmokrassar.postssystem.core.post.*
 import com.insanusmokrassar.postssystem.core.post.repo.WritePostsRepo
-import com.insanusmokrassar.postssystem.ktor.client.createStandardWebsocketFlow
+import com.insanusmokrassar.postssystem.ktor.client.*
 import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
 import io.ktor.client.HttpClient
 import io.ktor.client.features.websocket.WebSockets
@@ -35,25 +35,21 @@ class WritePostsRepoKtorClient (
         standardKtorSerialFormat.load(RegisteredPost.serializer(), it)
     }
 
-    override suspend fun createPost(post: Post): RegisteredPost? = client.post<ByteArray> {
-        url("$baseUrl/$createPostRoute")
-        body = standardKtorSerialFormat.dump(Post.serializer(), post)
-    }.let {
-        standardKtorSerialFormat.load(RegisteredPost.serializer().nullable, it)
-    }
+    override suspend fun createPost(post: Post): RegisteredPost? = client.unipost(
+        "$baseUrl/$createPostRoute",
+        BodyPair(Post.serializer(), post),
+        RegisteredPost.serializer().nullable
+    )
 
-    override suspend fun deletePost(id: PostId): Boolean = client.post<ByteArray> {
-        url("$baseUrl/$deletePostRoute")
-        body = standardKtorSerialFormat.dump(PostId.serializer(), id)
-    }.let {
-        standardKtorSerialFormat.load(Boolean.serializer(), it)
-    }
-
-    override suspend fun updatePostContent(postId: PostId, post: Post): Boolean = client.post<ByteArray> {
-        url("$baseUrl/$updatePostContentRoute")
-        body = standardKtorSerialFormat.dump(UpdatePostObject.serializer(), UpdatePostObject(postId, post))
-    }.let {
-        standardKtorSerialFormat.load(Boolean.serializer(), it)
-    }
+    override suspend fun deletePost(id: PostId): Boolean = client.unipost(
+        "$baseUrl/$deletePostRoute",
+        BodyPair(PostId.serializer(), id),
+        Boolean.serializer()
+    )
 
+    override suspend fun updatePostContent(postId: PostId, post: Post): Boolean = client.unipost(
+        "$baseUrl/$updatePostContentRoute",
+        BodyPair(UpdatePostObject.serializer(), UpdatePostObject(postId, post)),
+        Boolean.serializer()
+    )
 }
diff --git a/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/client/StandardHttpClientGetPost.kt b/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/client/StandardHttpClientGetPost.kt
new file mode 100644
index 00000000..ed42c57c
--- /dev/null
+++ b/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/client/StandardHttpClientGetPost.kt
@@ -0,0 +1,28 @@
+package com.insanusmokrassar.postssystem.ktor.client
+
+import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
+import io.ktor.client.HttpClient
+import io.ktor.client.request.get
+import io.ktor.client.request.post
+import kotlinx.serialization.*
+
+typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
+
+suspend fun <ResultType> HttpClient.uniget(
+    url: String,
+    resultDeserializer: DeserializationStrategy<ResultType>
+) = get<ByteArray>(
+    url
+).let {
+    standardKtorSerialFormat.load(resultDeserializer, it)
+}
+
+suspend fun <BodyType, ResultType> HttpClient.unipost(
+    url: String,
+    bodyInfo: BodyPair<BodyType>,
+    resultDeserializer: DeserializationStrategy<ResultType>
+) = post<ByteArray>(url) {
+    body = standardKtorSerialFormat.dump(bodyInfo.first, bodyInfo.second)
+}.let {
+    standardKtorSerialFormat.load(resultDeserializer, it)
+}