How are you abstracting Ktor’s `HttpClient`? I wou...
# ktor
k
How are you abstracting Ktor’s
HttpClient
? I would like to hide it behind interface for testing and maintainability purposes, but I can’t because its methods are defined as a
inline reified
Copy code
interface HttpClient {
    suspend fun <T> execute(endpoint: Endpoint): Resource<T>
}

class KtorHttpClient(private val ktor: Ktor) : HttpClient {

    override suspend fun <T> execute(endpoint: Endpoint): Resource<T> =
        wrapIntoResource {
            ktor.request(endpoint.toHttpRequest())
        }

    private suspend fun <T> wrapIntoResource(executor: suspend () -> T): Resource<T> =
        try {
            Resource.Data(executor())
        } catch (exception: Exception) {
            Resource.Error(RepositoryError.handle(exception))
        }
}
g
your execute also should be inline and reified to work
the reason for this is that request required instance of KType for deserialization
you can pass it manually as additional parameter
But honestly I don’t think that it very scalable solution, it will require duplicate a lot of APIs
for testing it’s possible to create HttpClient with MockEngine, which makes client perfectly testable
☝️ 1
in terms of maintainability, I believe it will make things a lot worse, yes it will hide many APIs, but you have to support own abstract layer for http client
k
Thank you for the answer. The reason of such this is that I wanted to achieve a flowing structure: 1. There are
Api
classes that contains functions returning
Endpoint
instances 2.
Endpoint
class describes an endpoint, it has:
baseUrl
,
method
,
path
,
body
etc. (return type as KType?) 3. My
HttpClient
accepts `Endpoint`s and returns
Resource<T>
4.
Resource<T>
is either
Data
or
Error
Using this structure everything is easily swappable and testable, which is very important for me, because I’m working on a multiplatform project where mocking is hard Of course I can test
HttpClient
with
MockEngine
and I’m doing it right now (testing if for example on
OK
response there is
Resource.Data
returned), but in for example
Repository
class where I’m using
HttpClient
I would like to easily swap its instance with mocked one
g
but in for example 
Repository
 class where I’m using 
HttpClient
 I would like to easily swap its instance with mocked one
But you still can do this by inject HttpClient instance to repository
Resource<T>
 is either 
Data
 or 
Error
I think you can achieve it with custom adapter for deserialization without wrapping whole API
j
Or add a datasource layer to deal with this. You may want to have multiple datasources in your repository layer. Maybe later you can add cache strategy…
g
but if you want redefine way to pass params, then yes, you need something custom, so you cannot use simple
request
function, just go to declaration, you will see how it implemented