Last active
August 13, 2023 16:48
-
-
Save aoriani/f20755828988965af3dcb0c14b7c8666 to your computer and use it in GitHub Desktop.
A simple implementation of a Retrofit-like library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.fasterxml.jackson.databind.DeserializationFeature | |
import com.fasterxml.jackson.databind.ObjectMapper | |
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper | |
import okhttp3.HttpUrl | |
import okhttp3.OkHttpClient | |
import okhttp3.Request | |
import java.lang.reflect.Method | |
import java.lang.reflect.ParameterizedType | |
import java.lang.reflect.Proxy | |
@Target(AnnotationTarget.FUNCTION) | |
@Retention(AnnotationRetention.RUNTIME) | |
annotation class GET(val baseUrl: String) | |
@Target(AnnotationTarget.VALUE_PARAMETER) | |
@Retention(AnnotationRetention.RUNTIME) | |
annotation class Query(val parameterName: String) | |
interface Call<out T> { | |
fun execute(): T | |
} | |
class SimpleRetrofit { | |
val objectMapper: ObjectMapper = jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) | |
val httpClient = OkHttpClient() | |
private fun createRequest(method: Method, args: Array<Any?>): Request { | |
val baseUrl = method.getAnnotation(GET::class.java).baseUrl | |
val paramNames = method.parameterAnnotations.flatten().map { (it as Query).parameterName } | |
val url = HttpUrl.parse(baseUrl).newBuilder().apply { | |
paramNames.forEachIndexed { index, paramName -> addQueryParameter(paramName, args[index].toString()) } | |
}.build() | |
return Request.Builder().url(url).build() | |
} | |
private fun <T> createCall(request: Request, responseClass: Class<T>): Call<T> { | |
return object : Call<T> { | |
override fun execute(): T { | |
val response = httpClient.newCall(request).execute().body().string() | |
return objectMapper.readValue(response, responseClass) | |
} | |
} | |
} | |
private fun extractResponseType(method: Method) = (method.genericReturnType as ParameterizedType).actualTypeArguments[0] as Class<*> | |
fun <T> createService(serviceClass: Class<T>): T = Proxy.newProxyInstance(serviceClass.classLoader, arrayOf(serviceClass)) { | |
thiz: Any, method: Method, args: Array<Any?> -> | |
val request = createRequest(method, args) | |
val responseType = extractResponseType(method) | |
createCall(request, responseType) | |
} as T | |
} | |
data class Weather(val main: Main) | |
data class Main(val temp: Double) | |
data class UvIndex(val value: Double) | |
interface OpenWeatherMapApi { | |
@GET("http://samples.openweathermap.org/data/2.5/weather") | |
fun getWeather(@Query("q") city: String, @Query("appid") apiKey: String): Call<Weather> | |
@GET("http://samples.openweathermap.org/data/2.5/uvi") | |
fun getUvIndex(@Query("lat") lat: Double, @Query("lon") lon: Double, @Query("appid") apiKey: String): Call<UvIndex> | |
} | |
fun main(args: Array<String>) { | |
val API_KEY = "YOUK KEY" | |
val service = SimpleRetrofit().createService(OpenWeatherMapApi::class.java) | |
val weather = service.getWeather("London", API_KEY).execute() | |
val uvIndex = service.getUvIndex(37.75, -122.37, API_KEY).execute() | |
println(weather) | |
println(uvIndex) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment