aboutsummaryrefslogtreecommitdiff
path: root/shared/src
diff options
context:
space:
mode:
authorIsidro Henoch <imhenoch@protonmail.com>2021-12-05 14:47:54 -0600
committerIsidro Henoch <imhenoch@protonmail.com>2021-12-05 14:48:48 -0600
commit4e0ca515a827220f9275f28649d83c1b09e44fcb (patch)
tree5afdac1a83daa31562f3b30438f2bbb85af2b667 /shared/src
parent477111a55b81a05bfc00857f0d4a3d6534c80396 (diff)
downloadetbsa-trackermap-mobile-4e0ca515a827220f9275f28649d83c1b09e44fcb.tar.gz
etbsa-trackermap-mobile-4e0ca515a827220f9275f28649d83c1b09e44fcb.tar.bz2
etbsa-trackermap-mobile-4e0ca515a827220f9275f28649d83c1b09e44fcb.zip
Adds x-www-form-urlencoded support to http client
- Installs a serialization library - Installs a ktor logging library - Updates ApiClient to support x-www-form-urlencoded requests - Serializes the User data model
Diffstat (limited to 'shared/src')
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/apis/SessionApi.kt8
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt58
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/User.kt52
3 files changed, 77 insertions, 41 deletions
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/apis/SessionApi.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/apis/SessionApi.kt
index d740a58..f80135d 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/apis/SessionApi.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/apis/SessionApi.kt
@@ -11,6 +11,8 @@
*/
package mx.trackermap.TrackerMap.client.apis
+import io.ktor.client.request.forms.FormDataContent
+import io.ktor.http.Parameters
import mx.trackermap.TrackerMap.client.models.User
import mx.trackermap.TrackerMap.client.infrastructure.*
@@ -73,10 +75,10 @@ class SessionApi(basePath: kotlin.String = "https://demo.traccar.org/api") : Api
* @return User
*/
@Suppress("UNCHECKED_CAST")
- suspend fun sessionPost(email: kotlin.String, password: kotlin.String): User {
- val localVariableBody: kotlin.Any? = mapOf("email" to "$email", "password" to "$password")
+ suspend fun sessionPost(email: String, password: String): User {
+ val localVariableBody = mapOf("email" to email, "password" to password)
- val localVariableHeaders: kotlin.collections.Map<kotlin.String, kotlin.String> = mapOf("Content-Type" to "multipart/form-data")
+ val localVariableHeaders = mapOf("Content-Type" to "application/x-www-form-urlencoded")
val localVariableConfig = RequestConfig(
RequestMethod.POST,
"/session", headers = localVariableHeaders
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt
index c44d473..91d0aa2 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt
@@ -4,10 +4,17 @@ import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.cio.*
import io.ktor.client.features.json.*
+import io.ktor.client.features.json.serializer.KotlinxSerializer
+import io.ktor.client.features.logging.DEFAULT
+import io.ktor.client.features.logging.LogLevel
+import io.ktor.client.features.logging.Logger
+import io.ktor.client.features.logging.Logging
import io.ktor.client.request.*
+import io.ktor.client.request.forms.FormDataContent
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.util.*
+import kotlinx.serialization.json.Json as KotlinJson
open class ApiClient(val baseUrl: String) {
companion object {
@@ -15,24 +22,41 @@ open class ApiClient(val baseUrl: String) {
protected const val ApiAccept = "Accept"
protected const val ApiJsonMediaType = "application/json"
protected const val ApiFormDataMediaType = "multipart/form-data"
+ protected const val ApiFormURLType = "application/x-www-form-urlencoded"
protected const val ApiXmlMediaType = "application/xml"
val client: HttpClient = HttpClient(CIO) {
- install(JsonFeature)
+ install(JsonFeature) {
+ serializer = KotlinxSerializer(
+ KotlinJson {
+ ignoreUnknownKeys = true
+ }
+ )
+ }
+ install(Logging) {
+ logger = Logger.DEFAULT
+ level = LogLevel.HEADERS
+ }
}
val defaultHeaders: Map<String, String> =
mapOf(
ApiContentType to ApiJsonMediaType,
- ApiAccept to ApiJsonMediaType)
+ ApiAccept to ApiJsonMediaType
+ )
val jsonHeaders: Map<String, String> =
mapOf(
ApiContentType to ApiJsonMediaType,
- ApiAccept to ApiJsonMediaType)
+ ApiAccept to ApiJsonMediaType
+ )
}
- protected inline fun <reified T> fillRequest(requestBuilder: HttpRequestBuilder, content: T, mediaType: String = ApiJsonMediaType) {
+ protected inline fun <reified T> fillRequest(
+ requestBuilder: HttpRequestBuilder,
+ content: T,
+ mediaType: String = ApiJsonMediaType
+ ) {
when {
mediaType == ApiFormDataMediaType && content is Map<*, *> -> {
val parametersBuilder = ParametersBuilder()
@@ -51,6 +75,13 @@ open class ApiClient(val baseUrl: String) {
requestBuilder.body = content
}
}
+ mediaType == ApiFormURLType && content is Map<*, *> -> {
+ val parametersBuilder = ParametersBuilder()
+ content.forEach { item ->
+ parametersBuilder[item.key as String] = item.value as String
+ }
+ requestBuilder.body = FormDataContent(parametersBuilder.build())
+ }
mediaType == ApiXmlMediaType -> TODO("xml not currently supported.")
// TODO: this should be extended with other serializers
@@ -58,7 +89,10 @@ open class ApiClient(val baseUrl: String) {
}
}
- protected suspend inline fun <reified T : Any?> request(requestConfig: RequestConfig, body: Any? = null): ApiInfrastructureResponse<T?> {
+ protected suspend inline fun <reified T : Any?> request(
+ requestConfig: RequestConfig,
+ body: Any? = null
+ ): ApiInfrastructureResponse<T?> {
val httpUrl: Url
try {
httpUrl = Url(baseUrl)
@@ -67,7 +101,7 @@ open class ApiClient(val baseUrl: String) {
}
val urlBuilder = URLBuilder(httpUrl)
- .path(requestConfig.path.trimStart('/'))
+ .path("${httpUrl.encodedPath.trimStart('/')}${requestConfig.path}")
requestConfig.query.forEach { query ->
query.value.forEach { queryValue ->
@@ -76,7 +110,7 @@ open class ApiClient(val baseUrl: String) {
}
val url = urlBuilder.build()
- val headers = requestConfig.headers + defaultHeaders
+ val headers = defaultHeaders + requestConfig.headers
if (headers[ApiContentType] ?: "" == "") {
throw IllegalStateException("Missing Content-Type header. This is required.")
@@ -121,10 +155,6 @@ open class ApiClient(val baseUrl: String) {
}
}
- headers.forEach { header ->
- request.headers[header.key] = header.value
- }
-
val response: HttpResponse = client.request(request)
// TODO: handle specific mapping types. e.g. Map<int, Class<?>>
@@ -141,11 +171,13 @@ open class ApiClient(val baseUrl: String) {
in 200..299 -> return Success(
response.receive(),
response.status.value,
- response.headers.toMap())
+ response.headers.toMap()
+ )
in 400..499 -> return ClientError(
response.receive(),
response.status.value,
- response.headers.toMap())
+ response.headers.toMap()
+ )
else -> return ServerError(
null,
response.receive(),
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/User.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/User.kt
index 926ec9d..1bda398 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/User.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/models/User.kt
@@ -11,6 +11,10 @@
*/
package mx.trackermap.TrackerMap.client.models
+import kotlinx.datetime.LocalDate
+import kotlinx.serialization.Contextual
+import kotlinx.serialization.Serializable
+
/**
*
@@ -37,30 +41,28 @@ package mx.trackermap.TrackerMap.client.models
* @param token
* @param attributes
*/
+@Serializable
data class User (
-
- val id: kotlin.Int? = null,
- val name: kotlin.String? = null,
- val email: kotlin.String? = null,
- val phone: kotlin.String? = null,
- val readonly: kotlin.Boolean? = null,
- val administrator: kotlin.Boolean? = null,
- val map: kotlin.String? = null,
- val latitude: java.math.BigDecimal? = null,
- val longitude: java.math.BigDecimal? = null,
- val zoom: kotlin.Int? = null,
- val password: kotlin.String? = null,
- val twelveHourFormat: kotlin.Boolean? = null,
- val coordinateFormat: kotlin.String? = null,
- val disabled: kotlin.Boolean? = null,
+ val id: Int? = null,
+ val name: String? = null,
+ val email: String? = null,
+ val phone: String? = null,
+ val readonly: Boolean? = null,
+ val administrator: Boolean? = null,
+ val map: String? = null,
+ val latitude: Double? = null,
+ val longitude: Double? = null,
+ val zoom: Int? = null,
+ val password: String? = null,
+ val twelveHourFormat: Boolean? = null,
+ val coordinateFormat: String? = null,
+ val disabled: Boolean? = null,
/* in IS0 8601 format. eg. `1963-11-22T18:30:00Z` */
- val expirationTime: java.time.LocalDateTime? = null,
- val deviceLimit: kotlin.Int? = null,
- val userLimit: kotlin.Int? = null,
- val deviceReadonly: kotlin.Boolean? = null,
- val limitCommands: kotlin.Boolean? = null,
- val poiLayer: kotlin.String? = null,
- val token: kotlin.String? = null,
- val attributes: kotlin.Any? = null
-) {
-} \ No newline at end of file
+ val expirationTime: LocalDate? = null,
+ val deviceLimit: Int? = null,
+ val userLimit: Int? = null,
+ val deviceReadonly: Boolean? = null,
+ val limitCommands: Boolean? = null,
+ val poiLayer: String? = null,
+ val token: String? = null
+) \ No newline at end of file