Last active
August 4, 2018 19:36
-
-
Save nomisRev/539dc9f7c306571e0bf6405f440efc2d to your computer and use it in GitHub Desktop.
Retrofit Call Adapter
This file contains hidden or 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 arrow.InstanceParametrizedType | |
import arrow.instance | |
import retrofit2.Converter | |
import okhttp3.ResponseBody | |
import helios.core.Json | |
import retrofit2.Retrofit | |
import java.lang.reflect.Type | |
import okhttp3.RequestBody | |
import helios.typeclasses.Encoder | |
import okhttp3.MediaType | |
object HeliosConverterFactory { | |
@JvmStatic | |
@JvmName("create") | |
operator fun invoke() = object : Converter.Factory() { | |
override fun responseBodyConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit) = HeliosResponseBodyConverter | |
override fun requestBodyConverter(type: Type, parameterAnnotations: Array<out Annotation>, methodAnnotations: Array<out Annotation>?, retrofit: Retrofit): Converter<*, RequestBody>? { | |
val encoder: Encoder<*> = instance(InstanceParametrizedType(Encoder::class.java, listOf(type))) | |
return heliosRequesBodyConverter(encoder) | |
} | |
} | |
} | |
private object HeliosResponseBodyConverter : Converter<ResponseBody, Json> { | |
override fun convert(value: ResponseBody): Json = value.use { | |
Json.parseFromString(value.string()).fold({ throw it }, { it }) | |
} | |
} | |
private val MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8") | |
private fun <T> heliosRequesBodyConverter(encoder: Encoder<T>) = Converter<T, RequestBody> { value -> | |
RequestBody.create(MEDIA_TYPE, encoder.encode(value).toJsonString()) | |
} |
This file contains hidden or 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 arrow.InstanceParametrizedType | |
import arrow.instance | |
import retrofit2.Converter | |
import okhttp3.ResponseBody | |
import helios.core.Json | |
import retrofit2.Retrofit | |
import java.lang.reflect.Type | |
import okhttp3.RequestBody | |
import helios.typeclasses.Encoder | |
import okhttp3.MediaType | |
inline fun <reified F> heliosCallAdapterFactory(AE: ApplicativeError<F, Throwable> = applicativeError()) = object : CallAdapter.Factory() { | |
override fun get(returnType: Type, annotations: Array<out Annotation>, retrofit: Retrofit): CallAdapter<*, *>? = | |
if (rawType(returnType) == HK::class.java) createBodyCallAdapter(returnType, AE) | |
else null | |
inline fun <reified F> createBodyCallAdapter(returnType: Type, AE: ApplicativeError<F, Throwable>): CallAdapter<Json, HK<F, Json>>? { | |
val (left, _) = (returnType as? ParameterizedType) | |
?.let { parameterUpperBound(0, it) to parameterUpperBound(1, it) } | |
?.takeIf { (_, right) -> right == Json::class.java } | |
?: throw IllegalStateException("HK return type must be parameterized as HK<F, Json>") | |
val newLeft = left?.let(::findF) | |
if (newLeft != F::class.java) return null | |
return object : CallAdapter<Json, HK<F, Json>> { | |
override fun responseType(): Type = Json::class.java | |
override fun adapt(call: Call<Json>): HK<F, Json> = AE.catch { | |
val response = call.execute() | |
if (response.isSuccessful) response.body()!! | |
else throw HttpException(response) | |
} | |
} | |
} | |
private tailrec fun findF(type: Type?): Type? { | |
return if ((type as? ParameterizedType)?.rawType == HK::class.java) { | |
val unwrappedType = (type as? ParameterizedType)?.let { parameterUpperBound(0, it) } | |
findF(unwrappedType) | |
} | |
else type | |
} | |
private fun parameterUpperBound(index: Int, type: ParameterizedType): Type? { | |
val types = type.actualTypeArguments | |
if (index < 0 || index >= types.size) return null | |
val paramType = types[index] | |
return if (paramType is WildcardType) paramType.upperBounds[0] else paramType | |
} | |
private fun rawType(type: Type): Class<*>? = when (type) { | |
is Class<*> -> type | |
is ParameterizedType -> type.rawType as? Class<*> | |
is GenericArrayType -> java.lang.reflect.Array.newInstance(rawType(type.genericComponentType), 0).javaClass | |
is TypeVariable<*> -> Any::class.java | |
is WildcardType -> rawType(type.upperBounds[0]) | |
else -> null | |
} | |
} |
This file contains hidden or 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 arrow.core.EitherHK | |
import arrow.core.EitherKind | |
import arrow.core.ev | |
import arrow.data.TryHK | |
import arrow.data.TryKind | |
import arrow.data.ev | |
import arrow.effects.IO | |
import arrow.effects.IOHK | |
import arrow.effects.IOKind | |
import arrow.effects.ObservableKW | |
import arrow.effects.ObservableKWHK | |
import arrow.effects.ObservableKWKind | |
import arrow.effects.ev | |
import helios.core.Json | |
import retrofit2.http.GET | |
import retrofit2.http.Headers | |
import retrofit2.http.Path | |
import retrofit2.Retrofit | |
interface TravisNetworkService { | |
@Headers("Accept: application/vnd.travis-ci.2+json", "User-Agent: android") | |
@GET("repos/{username}") | |
fun listRepos(@Path("username") username: String): TryKind<Json> | |
@Headers("Accept: application/vnd.travis-ci.2+json", "User-Agent: android") | |
@GET("repos/{repoId}") | |
fun repo(@Path("repoId") repoId: String): EitherKind<Throwable, Json> | |
@Headers("Accept: application/vnd.travis-ci.2+json", "User-Agent: android") | |
@GET("repos/{repoId}") | |
fun ioRepo(@Path("repoId") repoId: String): IOKind<Json> | |
@Headers("Accept: application/vnd.travis-ci.2+json", "User-Agent: android") | |
@GET("repos/{repoId}") | |
fun rxRepo(@Path("repoId") repoId: String): ObservableKWKind<Json> | |
} | |
fun main(args: Array<String>) { | |
val retrofit = Retrofit.Builder() | |
.baseUrl("https://api.travis-ci.org/") | |
.addConverterFactory(HeliosConverterFactory()) | |
.addCallAdapterFactory(heliosCallAdapterFactory<TryHK>()) | |
.addCallAdapterFactory(heliosCallAdapterFactory<EitherHK>()) | |
.addCallAdapterFactory(heliosCallAdapterFactory<IOHK>()) | |
.addCallAdapterFactory(heliosCallAdapterFactory<ObservableKWHK>()) | |
.build() | |
val service = retrofit.create(TravisNetworkService::class.java) | |
service.listRepos("arrow-kt").ev() | |
.fold(::println, ::println) | |
service.repo("12690991") | |
.ev() | |
.fold(::println, ::println) | |
service.ioRepo("0") | |
.let(::println) | |
service.rxRepo("0") | |
.ev() | |
.observable | |
.subscribe(::println, ::println) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment