From 4cc4d3d4ec19b6e2276c9cea89a1935641d692f0 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Mon, 31 Aug 2020 00:29:34 +0600
Subject: [PATCH 1/4] commonize url query parameters encoding/decoding

---
 .../ktor/client/StandardHttpClientGetPost.kt  |  8 +++--
 .../postssystem/ktor/HexConversations.kt      |  6 ----
 .../ktor/server/ServerRoutingShortcuts.kt     | 35 +++++++++++--------
 .../client/crud/KtorReadStandardCrudRepo.kt   |  5 +--
 .../server/crud/KtorReadStandardCrudRepo.kt   |  4 +--
 ...onfigureOneToManyReadKeyValueRepoRoutes.kt | 20 +++++------
 6 files changed, 41 insertions(+), 37 deletions(-)
 delete mode 100644 ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/HexConversations.kt

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
index 77e38e75..2fbb2133 100644
--- 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
@@ -4,8 +4,7 @@ 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.DeserializationStrategy
-import kotlinx.serialization.SerializationStrategy
+import kotlinx.serialization.*
 
 typealias BodyPair<T> = Pair<SerializationStrategy<T>, T>
 
@@ -18,6 +17,11 @@ suspend fun <ResultType> HttpClient.uniget(
     standardKtorSerialFormat.decodeFromByteArray(resultDeserializer, it)
 }
 
+fun <T> SerializationStrategy<T>.encodeUrlQueryValue(value: T) = standardKtorSerialFormat.encodeToHexString(
+    this,
+    value
+)
+
 suspend fun <BodyType, ResultType> HttpClient.unipost(
     url: String,
     bodyInfo: BodyPair<BodyType>,
diff --git a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/HexConversations.kt b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/HexConversations.kt
deleted file mode 100644
index eb7cb1d3..00000000
--- a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/HexConversations.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.insanusmokrassar.postssystem.ktor
-
-import kotlinx.serialization.*
-
-fun <T> T.toHex(with: SerializationStrategy<T>) = standardKtorSerialFormat.encodeToHexString(with, this)
-fun <T> String.fromHex(with: DeserializationStrategy<T>): T = standardKtorSerialFormat.decodeFromHexString(with, this)
diff --git a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/ServerRoutingShortcuts.kt b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/ServerRoutingShortcuts.kt
index 78336b61..7aacd5af 100644
--- a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/ServerRoutingShortcuts.kt
+++ b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/ServerRoutingShortcuts.kt
@@ -1,14 +1,12 @@
 package com.insanusmokrassar.postssystem.ktor.server
 
-import com.insanusmokrassar.postssystem.ktor.fromHex
 import com.insanusmokrassar.postssystem.ktor.standardKtorSerialFormat
 import io.ktor.application.ApplicationCall
 import io.ktor.http.HttpStatusCode
 import io.ktor.response.respond
 import io.ktor.response.respondBytes
 import io.ktor.util.toByteArray
-import kotlinx.serialization.DeserializationStrategy
-import kotlinx.serialization.SerializationStrategy
+import kotlinx.serialization.*
 
 suspend fun <T> ApplicationCall.unianswer(
     answerSerializer: SerializationStrategy<T>,
@@ -27,18 +25,6 @@ suspend fun <T> ApplicationCall.uniload(
     request.receiveChannel().toByteArray()
 )
 
-fun <T> ApplicationCall.uniloadFromQuery(
-    parameterName: String,
-    deserializer: DeserializationStrategy<T>
-): T? = getQueryParameter(parameterName) ?.fromHex(deserializer)
-
-suspend fun <T> ApplicationCall.uniloadFromQueryOrSendError(
-    parameterName: String,
-    deserializer: DeserializationStrategy<T>
-): T? = uniloadFromQuery(parameterName, deserializer) ?: null.also {
-    respond(HttpStatusCode.BadRequest, "Request query parameters must contains $parameterName")
-}
-
 suspend fun ApplicationCall.getParameterOrSendError(
     field: String
 ) = parameters[field].also {
@@ -58,3 +44,22 @@ suspend fun ApplicationCall.getQueryParameterOrSendError(
         respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
     }
 }
+
+fun <T> ApplicationCall.decodeUrlQueryValue(
+    field: String,
+    deserializer: DeserializationStrategy<T>
+) = getQueryParameter(field) ?.let {
+    standardKtorSerialFormat.decodeFromHexString(
+        deserializer,
+        it
+    )
+}
+
+fun <T> ApplicationCall.decodeUrlQueryValueOrSendError(
+    field: String,
+    deserializer: DeserializationStrategy<T>
+) = decodeUrlQueryValue(field, deserializer).also {
+    if (it == null) {
+        respond(HttpStatusCode.BadRequest, "Request query parameters must contains $field")
+    }
+}
diff --git a/utils/repos/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/client/crud/KtorReadStandardCrudRepo.kt b/utils/repos/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/client/crud/KtorReadStandardCrudRepo.kt
index 511dd3a2..65be8c49 100644
--- a/utils/repos/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/client/crud/KtorReadStandardCrudRepo.kt
+++ b/utils/repos/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/client/crud/KtorReadStandardCrudRepo.kt
@@ -1,6 +1,7 @@
 package com.insanusmokrassar.postssystem.utils.repos.ktor.client.crud
 
 import com.insanusmokrassar.postssystem.ktor.*
+import com.insanusmokrassar.postssystem.ktor.client.encodeUrlQueryValue
 import com.insanusmokrassar.postssystem.ktor.client.uniget
 import com.insanusmokrassar.postssystem.utils.common.pagination.Pagination
 import com.insanusmokrassar.postssystem.utils.common.pagination.PaginationResult
@@ -29,7 +30,7 @@ class KtorReadStandardCrudRepo<ObjectType, IdType> (
             baseUrl,
             getByIdRouting,
             mapOf(
-                "id" to id.toHex(idsSerializer)
+                "id" to idsSerializer.encodeUrlQueryValue(id)
             )
         ),
         objectsSerializerNullable
@@ -40,7 +41,7 @@ class KtorReadStandardCrudRepo<ObjectType, IdType> (
             baseUrl,
             containsRouting,
             mapOf(
-                "id" to id.toHex(idsSerializer)
+                "id" to idsSerializer.encodeUrlQueryValue(id)
             )
         ),
         Boolean.serializer()
diff --git a/utils/repos/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/server/crud/KtorReadStandardCrudRepo.kt b/utils/repos/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/server/crud/KtorReadStandardCrudRepo.kt
index bbdc19cc..6e20f166 100644
--- a/utils/repos/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/server/crud/KtorReadStandardCrudRepo.kt
+++ b/utils/repos/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/server/crud/KtorReadStandardCrudRepo.kt
@@ -28,7 +28,7 @@ fun <ObjectType, IdType> Route.configureReadStandardCrudRepoRoutes(
     }
 
     get(getByIdRouting) {
-        val id = call.uniloadFromQueryOrSendError(
+        val id = call.decodeUrlQueryValueOrSendError(
             "id",
             idsSerializer
         ) ?: return@get
@@ -40,7 +40,7 @@ fun <ObjectType, IdType> Route.configureReadStandardCrudRepoRoutes(
     }
 
     get(containsRouting) {
-        val id = call.uniloadFromQueryOrSendError(
+        val id = call.decodeUrlQueryValueOrSendError(
             "id",
             idsSerializer
         ) ?: return@get
diff --git a/utils/repos/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/server/one_to_many/ConfigureOneToManyReadKeyValueRepoRoutes.kt b/utils/repos/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/server/one_to_many/ConfigureOneToManyReadKeyValueRepoRoutes.kt
index 5f402ca7..1a48c7f8 100644
--- a/utils/repos/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/server/one_to_many/ConfigureOneToManyReadKeyValueRepoRoutes.kt
+++ b/utils/repos/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/server/one_to_many/ConfigureOneToManyReadKeyValueRepoRoutes.kt
@@ -22,11 +22,11 @@ fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
 
     get(getRoute) {
         val pagination = call.request.queryParameters.extractPagination
-        val key = call.uniloadFromQueryOrSendError(
+        val key = call.decodeUrlQueryValueOrSendError(
             keyParameterName,
             keySerializer
         ) ?: return@get
-        val reversed = call.uniloadFromQuery(
+        val reversed = call.decodeUrlQueryValue(
             reversedParameterName,
             Boolean.serializer()
         ) ?: false
@@ -39,7 +39,7 @@ fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
 
     get(keysRoute) {
         val pagination = call.request.queryParameters.extractPagination
-        val reversed = call.uniloadFromQuery(
+        val reversed = call.decodeUrlQueryValue(
             reversedParameterName,
             Boolean.serializer()
         ) ?: false
@@ -51,10 +51,10 @@ fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
     }
 
     get(containsByKeyRoute) {
-        val key = standardKtorSerialFormat.decodeFromHexString(
-            keySerializer,
-            call.getQueryParameterOrSendError(keyParameterName) ?: return@get
-        )
+        val key = call.decodeUrlQueryValueOrSendError(
+            keyParameterName,
+            keySerializer
+        ) ?: return@get
 
         call.unianswer(
             Boolean.serializer(),
@@ -63,11 +63,11 @@ fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
     }
 
     get(containsByKeyValueRoute) {
-        val key = call.uniloadFromQueryOrSendError(
+        val key = call.decodeUrlQueryValueOrSendError(
             keyParameterName,
             keySerializer
         ) ?: return@get
-        val value = call.uniloadFromQueryOrSendError(
+        val value = call.decodeUrlQueryValueOrSendError(
             valueParameterName,
             valueSealizer
         ) ?: return@get
@@ -79,7 +79,7 @@ fun <Key, Value> Route.configureOneToManyReadKeyValueRepoRoutes(
     }
 
     get(countByKeyRoute) {
-        val key = call.uniloadFromQueryOrSendError(
+        val key = call.decodeUrlQueryValueOrSendError(
             keyParameterName,
             keySerializer
         ) ?: return@get

From 617ac31ca11e15312bff1a4f3c3c7198bd45fc22 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Mon, 31 Aug 2020 00:40:58 +0600
Subject: [PATCH 2/4] hotfix

---
 .../ktor/server/ServerRoutingShortcuts.kt         |  2 +-
 .../one_to_many/KtorOneToManyReadKeyValueRepo.kt  | 15 ++++++++-------
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/ServerRoutingShortcuts.kt b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/ServerRoutingShortcuts.kt
index 7aacd5af..88bae92b 100644
--- a/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/ServerRoutingShortcuts.kt
+++ b/ktor/server/src/jvmMain/kotlin/com/insanusmokrassar/postssystem/ktor/server/ServerRoutingShortcuts.kt
@@ -55,7 +55,7 @@ fun <T> ApplicationCall.decodeUrlQueryValue(
     )
 }
 
-fun <T> ApplicationCall.decodeUrlQueryValueOrSendError(
+suspend fun <T> ApplicationCall.decodeUrlQueryValueOrSendError(
     field: String,
     deserializer: DeserializationStrategy<T>
 ) = decodeUrlQueryValue(field, deserializer).also {
diff --git a/utils/repos/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/client/one_to_many/KtorOneToManyReadKeyValueRepo.kt b/utils/repos/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/client/one_to_many/KtorOneToManyReadKeyValueRepo.kt
index 0df572fa..e0736e40 100644
--- a/utils/repos/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/client/one_to_many/KtorOneToManyReadKeyValueRepo.kt
+++ b/utils/repos/ktor/client/src/commonMain/kotlin/com/insanusmokrassar/postssystem/utils/repos/ktor/client/one_to_many/KtorOneToManyReadKeyValueRepo.kt
@@ -1,6 +1,7 @@
 package com.insanusmokrassar.postssystem.utils.repos.ktor.client.one_to_many
 
 import com.insanusmokrassar.postssystem.ktor.*
+import com.insanusmokrassar.postssystem.ktor.client.encodeUrlQueryValue
 import com.insanusmokrassar.postssystem.ktor.client.uniget
 import com.insanusmokrassar.postssystem.utils.common.pagination.Pagination
 import com.insanusmokrassar.postssystem.utils.common.pagination.PaginationResult
@@ -24,8 +25,8 @@ class KtorOneToManyReadKeyValueRepo<Key, Value> (
             baseUrl,
             getRoute,
             mapOf(
-                keyParameterName to k.toHex(keySerializer),
-                reversedParameterName to reversed.toHex(Boolean.serializer())
+                keyParameterName to keySerializer.encodeUrlQueryValue(k),
+                reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed)
             ) + pagination.asUrlQueryParts
         ),
         paginationValueResultSerializer
@@ -36,7 +37,7 @@ class KtorOneToManyReadKeyValueRepo<Key, Value> (
             baseUrl,
             keysRoute,
             mapOf(
-                reversedParameterName to reversed.toHex(Boolean.serializer())
+                reversedParameterName to Boolean.serializer().encodeUrlQueryValue(reversed)
             ) + pagination.asUrlQueryParts
         ),
         paginationKeyResultSerializer
@@ -46,7 +47,7 @@ class KtorOneToManyReadKeyValueRepo<Key, Value> (
         buildStandardUrl(
             baseUrl,
             containsByKeyRoute,
-            mapOf(keyParameterName to k.toHex(keySerializer))
+            mapOf(keyParameterName to keySerializer.encodeUrlQueryValue(k))
         ),
         Boolean.serializer()
     )
@@ -56,8 +57,8 @@ class KtorOneToManyReadKeyValueRepo<Key, Value> (
             baseUrl,
             containsByKeyValueRoute,
             mapOf(
-                keyParameterName to k.toHex(keySerializer),
-                valueParameterName to v.toHex(valueSerializer),
+                keyParameterName to keySerializer.encodeUrlQueryValue(k),
+                valueParameterName to valueSerializer.encodeUrlQueryValue(v),
             )
         ),
         Boolean.serializer()
@@ -68,7 +69,7 @@ class KtorOneToManyReadKeyValueRepo<Key, Value> (
             baseUrl,
             countByKeyRoute,
             mapOf(
-                keyParameterName to k.toHex(keySerializer)
+                keyParameterName to keySerializer.encodeUrlQueryValue(k)
             )
         ),
         Long.serializer()

From 9a5585957fa12575a468b22d6ade706a62eeb6ff Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Mon, 31 Aug 2020 00:46:53 +0600
Subject: [PATCH 3/4] update tools for urls

---
 .../postssystem/ktor/BuildStandardUrl.kt           | 14 ++++++++++++++
 .../postssystem/ktor/QueryParamsBuilder.kt         |  9 +++++++++
 2 files changed, 23 insertions(+)

diff --git a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/BuildStandardUrl.kt b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/BuildStandardUrl.kt
index 1204ec93..22be8bcc 100644
--- a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/BuildStandardUrl.kt
+++ b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/BuildStandardUrl.kt
@@ -7,3 +7,17 @@ fun buildStandardUrl(
 ) = "$basePart/$subpart".includeQueryParams(
     parameters
 )
+
+fun buildStandardUrl(
+    basePart: String,
+    subpart: String,
+    parameters: List<QueryParam>
+) = "$basePart/$subpart".includeQueryParams(
+    parameters
+)
+
+fun buildStandardUrl(
+    basePart: String,
+    subpart: String,
+    vararg parameters: QueryParam
+) = buildStandardUrl(basePart, subpart, parameters.toList())
diff --git a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/QueryParamsBuilder.kt b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/QueryParamsBuilder.kt
index 00210808..1d50ef62 100644
--- a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/QueryParamsBuilder.kt
+++ b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/QueryParamsBuilder.kt
@@ -1,13 +1,22 @@
 package com.insanusmokrassar.postssystem.ktor
 
+typealias QueryParam = Pair<String, String?>
 typealias QueryParams = Map<String, String?>
 
 val QueryParams.asUrlQuery: String
     get() = keys.joinToString("&") { "${it}${get(it) ?.let { value -> "=$value" }}" }
+
+val List<QueryParam>.asUrlQuery: String
+    get() = joinToString("&") { (key, value) -> "${key}${value ?.let { _ -> "=$value" }}" }
+
 fun String.includeQueryParams(
     queryParams: QueryParams
 ): String = "$this${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}"
 
+fun String.includeQueryParams(
+    queryParams: List<QueryParam>
+): String = "$this${if (contains("?")) "&" else "?"}${queryParams.asUrlQuery}"
+
 val String.parseUrlQuery: QueryParams
     get() = split("&").map {
         it.split("=").let { pair ->

From 14e0196c11320883a6c093f64292177b3a35e9a7 Mon Sep 17 00:00:00 2001
From: InsanusMokrassar <ovsyannikov.alexey95@gmail.com>
Date: Mon, 31 Aug 2020 00:51:48 +0600
Subject: [PATCH 4/4] update tools for urls

---
 .../insanusmokrassar/postssystem/ktor/PaginationInUrl.kt    | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/PaginationInUrl.kt b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/PaginationInUrl.kt
index fe957a2a..60cb0244 100644
--- a/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/PaginationInUrl.kt
+++ b/ktor/common/src/commonMain/kotlin/com/insanusmokrassar/postssystem/ktor/PaginationInUrl.kt
@@ -12,6 +12,12 @@ val Pagination.asUrlQueryParts
         "size" to size.toString()
     )
 
+val Pagination.asUrlQueryArrayParts
+    get() = arrayOf(
+        "page" to page.toString(),
+        "size" to size.toString()
+    )
+
 val Map<String, String?>.extractPagination: Pagination
     get() = SimplePagination(
         get("page") ?.toIntOrNull() ?: 0,