add simple converters to translate error response to an Either return type of retrofit service methods

authentification
maxime 1 year ago
parent 578c6f5ff5
commit 51f7c046ce

@ -12,7 +12,7 @@ android {
defaultConfig {
applicationId = "com.iqball.app"
minSdk = 21
minSdk = 28
targetSdk = 34
versionCode = 1
versionName = "1.0"

@ -9,6 +9,8 @@ import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.iqball.app.api.EitherBodyConverter
import com.iqball.app.api.EitherCallAdapterFactory
import com.iqball.app.api.service.IQBallService
import com.iqball.app.page.RegisterPage
@ -28,18 +30,10 @@ class MainActivity : ComponentActivity() {
val gson = Gson()
val retrofit = Retrofit.Builder()
.addConverterFactory(EitherBodyConverter.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(EitherCallAdapterFactory.create(gson))
.addConverterFactory(object : Converter.Factory() {
override fun responseBodyConverter(
type: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
return super.responseBodyConverter(type, annotations, retrofit)
}
})
.baseUrl("http://grospc:5254")
.baseUrl("https://iqball.maxou.dev/api/dotnet-master/")
.client(
OkHttpClient.Builder()
.addInterceptor { it.proceed(it.request()) }

@ -0,0 +1,37 @@
package com.iqball.app.api
import arrow.core.Either
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Converter
import retrofit2.Retrofit
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
class EitherBodyConverter : Converter.Factory() {
override fun responseBodyConverter(
type: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *>? {
if (type !is ParameterizedType) {
return null
}
if (type.rawType != Call::class.java) {
return null
}
val eitherType = type.actualTypeArguments.first()
if (eitherType !is ParameterizedType || eitherType.rawType != Either::class.java) {
return null
}
val eitherRightType = eitherType.actualTypeArguments[1]
return retrofit.nextResponseBodyConverter<Any>(this, eitherRightType, annotations)
}
companion object {
fun create() = EitherBodyConverter()
}
}

@ -1,5 +1,8 @@
package com.iqball.app.api
import android.os.Build
import androidx.annotation.RequiresApi
import arrow.core.Either
import com.google.gson.Gson
import com.skydoves.retrofit.adapters.arrow.EitherCallAdapterFactory
import retrofit2.Call
@ -13,13 +16,21 @@ class EitherCallAdapterFactory(private val gson: Gson) : CallAdapter.Factory() {
returnType: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit
): CallAdapter<*, *> {
): CallAdapter<*, *>? {
if (returnType !is ParameterizedType) {
return null
}
if (returnType.rawType != Call::class.java) {
return null
}
val eitherType = returnType.actualTypeArguments.first()
if (eitherType !is ParameterizedType || eitherType.rawType != Either::class.java) {
return null
}
return object : CallAdapter<Any, Any> {
override fun responseType(): Type = returnType
override fun adapt(call: Call<Any>): EitherCall<Any, Any> {
val callType = returnType as ParameterizedType
val eitherType = callType.actualTypeArguments.first() as ParameterizedType
return EitherCall(gson, eitherType, call)
}
}

@ -3,26 +3,26 @@ package com.iqball.app.api.service
import arrow.core.Either
import kotlinx.datetime.LocalDateTime
import kotlinx.serialization.Serializable
import retrofit2.Call
import retrofit2.HttpException
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
interface AuthService {
@Serializable
data class AuthResponse(val token: String, val expirationDate: LocalDateTime)
data class AuthResponse(val token: String, val expirationDate: String)
@Serializable
data class RegisterRequest(val username: String, val email: String, val password: String)
@POST("/auth/register")
@POST("auth/register")
suspend fun register(@Body req: RegisterRequest): APIResult<AuthResponse>
data class LoginRequest(val email: String, val password: String)
@POST("/auth/token")
suspend fun login(@Body req: LoginRequest): Call<AuthResponse>
@POST("auth/token")
suspend fun login(@Body req: LoginRequest): APIResult<AuthResponse>
}

@ -6,4 +6,4 @@ import retrofit2.Call
typealias ErrorResponseResult = Map<String, Array<String>>
typealias APIResult<R> = Either<ErrorResponseResult, R>
interface IQBallService : AuthService
interface IQBallService : AuthService, UserService

@ -0,0 +1,11 @@
package com.iqball.app.api.service
import retrofit2.http.GET
import retrofit2.http.Header
interface UserService {
data class UserDataResponse(val teams: List<Any>, val tactics: List<Any>)
@GET("user-data")
suspend fun getUserData(@Header("Authorization") auth: String): APIResult<UserDataResponse>
}

@ -8,8 +8,11 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import arrow.core.Either
import com.iqball.app.api.service.AuthService
import com.iqball.app.api.service.AuthService.RegisterRequest
import com.iqball.app.api.service.IQBallService
import com.iqball.app.session.Authentication
import com.iqball.app.session.MutableSession
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
@ -22,26 +25,30 @@ fun RegisterPage(service: IQBallService) {
var text by remember { mutableStateOf("No message !") }
runBlocking {
val result = service.register(RegisterRequest("abcdefg", "a@m.com", "123456"))
val result = service.login(AuthService.LoginRequest("maxime@mail.com", "123456"))
when (result) {
is Either.Left -> println("Error : " + result.value)
is Either.Right -> println("Success : " + result.value)
is Either.Left -> {
println("Error : " + result.value)
text = result.toString()
}
is Either.Right -> {
val token = result.value.token
val userDataResponse = service.getUserData(token)
when (userDataResponse) {
is Either.Left -> println("Error User Data : " + userDataResponse.value)
is Either.Right -> println("Success User Data : " + userDataResponse.value)
}
text = userDataResponse.toString()
}
}
println(result)
text = result.toString()
Log.i("%", result.toString())
}
Text(text = text)
}
suspend fun updateTextIn5Sec(setText: (String) -> Unit) = coroutineScope {
launch(Dispatchers.IO) {
delay(5000)
setText("test")
}
}

@ -0,0 +1,5 @@
package com.iqball.app.session
import kotlinx.datetime.LocalDateTime
data class Authentication(val token: String, val expirationDate: LocalDateTime)

@ -0,0 +1,5 @@
package com.iqball.app.session
interface MutableSession : Session {
override var auth: Authentication
}

@ -0,0 +1,5 @@
package com.iqball.app.session
interface Session {
val auth: Authentication
}
Loading…
Cancel
Save