Fix recursive calls to getAuthToken

This commit is contained in:
Kirill Kamakin
2022-04-05 19:09:37 +05:00
parent b3f7527884
commit d40793104f
7 changed files with 52 additions and 37 deletions

View File

@@ -23,7 +23,7 @@ class AuthDataSourceImpl @Inject constructor(
override suspend fun authenticate(username: String, password: String): String {
Timber.v("authenticate() called with: username = $username, password = $password")
val authService = authServiceFactory.provideService()
val authService = authServiceFactory.provideService(needAuth = false)
val response = sendRequest(authService, username, password)
val accessToken = parseToken(response)
Timber.v("authenticate() returned: $accessToken")

View File

@@ -1,21 +0,0 @@
package gq.kirmanak.mealient.data.network
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import timber.log.Timber
import javax.inject.Inject
class OkHttpBuilder @Inject constructor(
// Use @JvmSuppressWildcards because otherwise dagger can't inject it (https://stackoverflow.com/a/43149382)
private val authenticationInterceptor: AuthenticationInterceptor,
private val interceptors: Set<@JvmSuppressWildcards Interceptor>,
) {
fun buildOkHttp(): OkHttpClient {
Timber.v("buildOkHttp() called")
return OkHttpClient.Builder().apply {
addInterceptor(authenticationInterceptor)
for (interceptor in interceptors) addNetworkInterceptor(interceptor)
}.build()
}
}

View File

@@ -1,6 +1,8 @@
package gq.kirmanak.mealient.data.network
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import gq.kirmanak.mealient.di.AUTH_OK_HTTP
import gq.kirmanak.mealient.di.NO_AUTH_OK_HTTP
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
@@ -8,22 +10,25 @@ import okhttp3.OkHttpClient
import retrofit2.Retrofit
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
@Singleton
class RetrofitBuilder @Inject constructor(
private val okHttpClient: OkHttpClient,
@Named(AUTH_OK_HTTP) private val authOkHttpClient: OkHttpClient,
@Named(NO_AUTH_OK_HTTP) private val noAuthOkHttpClient: OkHttpClient,
private val json: Json
) {
@OptIn(ExperimentalSerializationApi::class)
fun buildRetrofit(baseUrl: String): Retrofit {
fun buildRetrofit(baseUrl: String, needAuth: Boolean): Retrofit {
Timber.v("buildRetrofit() called with: baseUrl = $baseUrl")
val contentType = "application/json".toMediaType()
val converterFactory = json.asConverterFactory(contentType)
val client = if (needAuth) authOkHttpClient else noAuthOkHttpClient
return Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.client(client)
.addConverterFactory(converterFactory)
.build()
}

View File

@@ -13,21 +13,28 @@ class RetrofitServiceFactory<T>(
private val baseURLStorage: BaseURLStorage,
) : ServiceFactory<T> {
private val cache: MutableMap<String, T> = mutableMapOf()
private val cache: MutableMap<ServiceParams, T> = mutableMapOf()
override suspend fun provideService(baseUrl: String?): T = runCatchingExceptCancel {
override suspend fun provideService(
baseUrl: String?,
needAuth: Boolean,
): T = runCatchingExceptCancel {
Timber.v("provideService() called with: baseUrl = $baseUrl, class = ${serviceClass.simpleName}")
val url = baseUrl ?: baseURLStorage.requireBaseURL()
synchronized(cache) { cache[url] ?: createService(url, serviceClass) }
val params = ServiceParams(url, needAuth)
synchronized(cache) { cache[params] ?: createService(params, serviceClass) }
}.getOrElse {
Timber.e(it, "provideService: can't provide service for $baseUrl")
throw NetworkError.MalformedUrl(it)
}
private fun createService(url: String, serviceClass: Class<T>): T {
Timber.v("createService() called with: url = $url, serviceClass = ${serviceClass.simpleName}")
val service = retrofitBuilder.buildRetrofit(url).create(serviceClass)
cache[url] = service
private fun createService(serviceParams: ServiceParams, serviceClass: Class<T>): T {
Timber.v("createService() called with: serviceParams = $serviceParams, serviceClass = ${serviceClass.simpleName}")
val (url, needAuth) = serviceParams
val service = retrofitBuilder.buildRetrofit(url, needAuth).create(serviceClass)
cache[serviceParams] = service
return service
}
data class ServiceParams(val baseUrl: String, val needAuth: Boolean)
}

View File

@@ -2,5 +2,5 @@ package gq.kirmanak.mealient.data.network
interface ServiceFactory<T> {
suspend fun provideService(baseUrl: String? = null): T
suspend fun provideService(baseUrl: String? = null, needAuth: Boolean = true): T
}

View File

@@ -4,19 +4,41 @@ import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import gq.kirmanak.mealient.data.network.OkHttpBuilder
import gq.kirmanak.mealient.data.network.AuthenticationInterceptor
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import javax.inject.Named
import javax.inject.Singleton
const val AUTH_OK_HTTP = "auth"
const val NO_AUTH_OK_HTTP = "noauth"
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun createOkHttp(okHttpBuilder: OkHttpBuilder): OkHttpClient =
okHttpBuilder.buildOkHttp()
@Named(AUTH_OK_HTTP)
fun createAuthOkHttp(
// Use @JvmSuppressWildcards because otherwise dagger can't inject it (https://stackoverflow.com/a/43149382)
interceptors: Set<@JvmSuppressWildcards Interceptor>,
authenticationInterceptor: AuthenticationInterceptor,
): OkHttpClient = OkHttpClient.Builder()
.addInterceptor(authenticationInterceptor)
.apply { for (interceptor in interceptors) addNetworkInterceptor(interceptor) }
.build()
@Provides
@Singleton
@Named(NO_AUTH_OK_HTTP)
fun createNoAuthOkHttp(
// Use @JvmSuppressWildcards because otherwise dagger can't inject it (https://stackoverflow.com/a/43149382)
interceptors: Set<@JvmSuppressWildcards Interceptor>,
): OkHttpClient = OkHttpClient.Builder()
.apply { for (interceptor in interceptors) addNetworkInterceptor(interceptor) }
.build()
@Provides
@Singleton

View File

@@ -5,15 +5,17 @@ import com.squareup.picasso.OkHttp3Downloader
import com.squareup.picasso.Picasso
import dagger.hilt.android.qualifiers.ApplicationContext
import gq.kirmanak.mealient.BuildConfig
import gq.kirmanak.mealient.di.AUTH_OK_HTTP
import okhttp3.OkHttpClient
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
@Singleton
class PicassoBuilder @Inject constructor(
@ApplicationContext private val context: Context,
private val okHttpClient: OkHttpClient
@Named(AUTH_OK_HTTP) private val okHttpClient: OkHttpClient
) {
fun buildPicasso(): Picasso {