Skip to content

Updated String Deserialization #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package jp.co.soramitsu.appxnetworking

import androidx.constraintlayout.solver.state.State.Chain
import jp.co.soramitsu.xnetworking.android.ChainInfoConstants
import jp.co.soramitsu.xnetworking.lib.datasources.blockexplorer.api.BlockExplorerRepository
import jp.co.soramitsu.xnetworking.lib.datasources.blockexplorer.api.models.AssetInfo
import jp.co.soramitsu.xnetworking.lib.engines.rest.api.RestClient
import jp.co.soramitsu.xnetworking.lib.engines.rest.api.models.AbstractRestServerRequest
import kotlinx.serialization.DeserializationStrategy
import jp.co.soramitsu.xnetworking.lib.engines.utils.JsonGetRequest
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.builtins.ListSerializer
Expand All @@ -18,12 +16,16 @@ class NetworkService(
) {

suspend fun getRequest() = restClient.get(
SimpleJSONGetRequestHolder(url = "https://www.github.com")
request = JsonGetRequest(
url = "https://www.github.com",
responseDeserializer = ListSerializer(AssetRemote.serializer())
)
)

suspend fun getAssets() = restClient.get(
SimpleJSONGetRequestHolder(
url = "https://raw.githubusercontent.com/soramitsu/fearless-utils/android/v2/chains/assets.json"
request = JsonGetRequest(
url = "https://raw.githubusercontent.com/soramitsu/fearless-utils/android/v2/chains/assets.json",
responseDeserializer = ListSerializer(AssetRemote.serializer())
)
)

Expand Down Expand Up @@ -82,11 +84,6 @@ class NetworkService(
// }
}

private data class SimpleJSONGetRequestHolder(
override val url: String,
override val responseDeserializer: DeserializationStrategy<List<AssetRemote>> = ListSerializer(AssetRemote.serializer())
): AbstractRestServerRequest<List<AssetRemote>>()

@Serializable
data class AssetRemote(
@SerialName("id")
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ apolloGraphQLVersion=3.8.2
sqlDelightVersion=1.5.5
ktorVersion=2.3.1
coroutineVersion=1.8.1
libVersion=1.0.3
libVersion=1.0.4

RELEASE_REPOSITORY_URL=https://nexus.iroha.tech/repository/maven-soramitsu/
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ abstract class RestClient {

@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
abstract suspend fun <T> post(
abstract suspend fun <T: Any> post(
request: AbstractRestServerRequest.WithBody<T>
): T

@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
abstract suspend fun <T> get(
abstract suspend fun <T: Any> get(
request: AbstractRestServerRequest<T>
): T
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.Transient
import kotlin.experimental.ExperimentalObjCRefinement
import kotlin.native.HiddenFromObjC
import kotlin.reflect.KClass

abstract class AbstractRestServerRequest<T> {
abstract class AbstractRestServerRequest<T: Any> {

open val bearerToken: String? = null

Expand All @@ -25,8 +26,14 @@ abstract class AbstractRestServerRequest<T> {
@Transient
abstract val responseDeserializer: DeserializationStrategy<T>

@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
@Transient
abstract val responseClazz: KClass<T>

/**
* Specific Implementation of equals that ignores @param #responseDeserializer
* and @param #responseClazz
*/
override fun equals(other: Any?): Boolean {
if (other !is AbstractRestServerRequest<*>)
Expand Down Expand Up @@ -65,7 +72,7 @@ abstract class AbstractRestServerRequest<T> {
return super.hashCode()
}

abstract class WithBody<Response>: AbstractRestServerRequest<Response>() {
abstract class WithBody<Response: Any>: AbstractRestServerRequest<Response>() {

abstract val requestContentType: RestClient.ContentType

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package jp.co.soramitsu.xnetworking.lib.engines.rest.impl

import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.RedirectResponseException
import io.ktor.client.plugins.ResponseException
import io.ktor.client.plugins.ServerResponseException
import io.ktor.client.request.accept
import io.ktor.client.request.bearerAuth
import io.ktor.client.request.get
Expand All @@ -24,15 +20,19 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationException
import kotlin.reflect.KClass

class RestClientImpl(
private val restClientConfig: AbstractRestClientConfig
): RestClient() {

private val client by httpClientBuilder { restClientConfig }

override suspend fun <T> post(request: AbstractRestServerRequest.WithBody<T>): T =
requestCatchingAndDeserialize(request.responseDeserializer) {
override suspend fun <T: Any> post(request: AbstractRestServerRequest.WithBody<T>): T =
requestCatchingAndDeserialize(
deserializer = request.responseDeserializer,
clazz = request.responseClazz
) {
client.post(request.url) {
if (request.requestContentType === ContentType.JSON)
contentType(io.ktor.http.ContentType.Application.Json)
Expand All @@ -58,8 +58,11 @@ class RestClientImpl(
}.bodyAsText()
}

override suspend fun <T> get(request: AbstractRestServerRequest<T>): T =
requestCatchingAndDeserialize(request.responseDeserializer) {
override suspend fun <T: Any> get(request: AbstractRestServerRequest<T>): T =
requestCatchingAndDeserialize(
deserializer = request.responseDeserializer,
clazz = request.responseClazz
) {
client.get(request.url) {
request.bearerToken.apply {
if (!this.isNullOrBlank())
Expand All @@ -84,16 +87,22 @@ class RestClientImpl(
}.bodyAsText()
}

private suspend fun <T> requestCatchingAndDeserialize(
private suspend fun <T: Any> requestCatchingAndDeserialize(
deserializer: DeserializationStrategy<T>,
clazz: KClass<T>,
block: suspend () -> String
) = withContext(Dispatchers.Default) {
try {
val value = withContext(Dispatchers.CommonIO) { block.invoke() }

if (clazz == String::class) {
@Suppress("UNCHECKED_CAST")
return@withContext value as T
}

restClientConfig.getOrCreateJsonConfig().decodeFromString(
deserializer = deserializer,
string = withContext(Dispatchers.CommonIO) {
block.invoke()
}
string = value
)
} catch (e: RestClientException.WithCode) {
throw e
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,100 @@ import jp.co.soramitsu.xnetworking.lib.engines.rest.api.models.AbstractRestServe
import kotlinx.serialization.DeserializationStrategy
import kotlin.experimental.ExperimentalObjCRefinement
import kotlin.native.HiddenFromObjC
import kotlin.reflect.KClass

@Suppress("FunctionName")
@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
class JsonPostRequest<T>(
override val url: String,
override val body: Any,
override val responseDeserializer: DeserializationStrategy<T>
): AbstractRestServerRequest.WithBody<T>() {
inline fun <reified Response: Any> JsonPostRequest(
url: String,
body: Any,
userAgent: String? = null,
bearerToken: String? = null,
headers: Map<String, String>? = null,
responseDeserializer: DeserializationStrategy<Response>
) = JsonPostRequestNonReified(
url = url,
body = body,
userAgent = userAgent,
bearerToken = bearerToken,
headers = headers,
responseDeserializer = responseDeserializer,
responseClazz = Response::class
)

@Suppress("FunctionName")
@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
fun <Response: Any> JsonPostRequestNonReified(
url: String,
body: Any,
userAgent: String? = null,
bearerToken: String? = null,
headers: Map<String, String>? = null,
responseDeserializer: DeserializationStrategy<Response>,
responseClazz: KClass<Response>
) = object : AbstractRestServerRequest.WithBody<Response>() {
override val bearerToken: String? = bearerToken

override val url: String = url

override val headers: Map<String, String>? = headers

override val userAgent: String? = userAgent

override val body: Any = body

override val responseDeserializer: DeserializationStrategy<Response> = responseDeserializer

override val responseClazz: KClass<Response> = responseClazz

override val requestContentType: RestClient.ContentType = RestClient.ContentType.JSON
}

@Suppress("FunctionName")
@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
class JsonGetRequest<Response>(
override val url: String,
override val headers: Map<String, String>? = null,
override val queryParams: Map<String, String>? = null,
override val responseDeserializer: DeserializationStrategy<Response>
): AbstractRestServerRequest<Response>()
inline fun <reified Response: Any> JsonGetRequest(
url: String,
userAgent: String? = null,
bearerToken: String? = null,
headers: Map<String, String>? = null,
queryParams: Map<String, String>? = null,
responseDeserializer: DeserializationStrategy<Response>
) = JsonGetRequestNonReified(
url = url,
userAgent = userAgent,
bearerToken = bearerToken,
headers = headers,
queryParams = queryParams,
responseDeserializer = responseDeserializer,
responseClazz = Response::class
)

@Suppress("FunctionName")
@OptIn(ExperimentalObjCRefinement::class)
@HiddenFromObjC
fun <Response: Any> JsonGetRequestNonReified(
url: String,
userAgent: String? = null,
bearerToken: String? = null,
headers: Map<String, String>? = null,
queryParams: Map<String, String>? = null,
responseDeserializer: DeserializationStrategy<Response>,
responseClazz: KClass<Response>
) = object: AbstractRestServerRequest<Response>() {
override val bearerToken: String? = bearerToken

override val url: String = url

override val headers: Map<String, String>? = headers

override val userAgent: String? = userAgent

override val queryParams: Map<String, String>? = queryParams

override val responseDeserializer: DeserializationStrategy<Response> = responseDeserializer

override val responseClazz: KClass<Response> = responseClazz
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ import jp.co.soramitsu.xnetworking.lib.engines.rest.api.models.AbstractRestServe
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.builtins.serializer
import kotlin.experimental.ExperimentalObjCName
import kotlin.reflect.KClass


@OptIn(ExperimentalObjCName::class)
@ObjCName("JsonGetRequest")
class JsonGetRequestIOS(
override val url: String,
override val headers: Map<String, String>? = null,
override val queryParams: Map<String, String>? = null,
override val queryParams: Map<String, String>? = null
): AbstractRestServerRequest<String>() {
override val responseDeserializer: DeserializationStrategy<String> = String.serializer()
override val responseClazz: KClass<String> = String::class
}

@OptIn(ExperimentalObjCName::class)
Expand All @@ -25,4 +27,5 @@ class JsonPostRequestIOS(
): AbstractRestServerRequest.WithBody<String>() {
override val requestContentType: RestClient.ContentType = RestClient.ContentType.JSON
override val responseDeserializer: DeserializationStrategy<String> = String.serializer()
override val responseClazz: KClass<String> = String::class
}