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 { override suspend fun authenticate(username: String, password: String): String {
Timber.v("authenticate() called with: username = $username, password = $password") 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 response = sendRequest(authService, username, password)
val accessToken = parseToken(response) val accessToken = parseToken(response)
Timber.v("authenticate() returned: $accessToken") 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 package gq.kirmanak.mealient.data.network
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory 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.ExperimentalSerializationApi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType import okhttp3.MediaType.Companion.toMediaType
@@ -8,22 +10,25 @@ import okhttp3.OkHttpClient
import retrofit2.Retrofit import retrofit2.Retrofit
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton import javax.inject.Singleton
@Singleton @Singleton
class RetrofitBuilder @Inject constructor( 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 private val json: Json
) { ) {
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
fun buildRetrofit(baseUrl: String): Retrofit { fun buildRetrofit(baseUrl: String, needAuth: Boolean): Retrofit {
Timber.v("buildRetrofit() called with: baseUrl = $baseUrl") Timber.v("buildRetrofit() called with: baseUrl = $baseUrl")
val contentType = "application/json".toMediaType() val contentType = "application/json".toMediaType()
val converterFactory = json.asConverterFactory(contentType) val converterFactory = json.asConverterFactory(contentType)
val client = if (needAuth) authOkHttpClient else noAuthOkHttpClient
return Retrofit.Builder() return Retrofit.Builder()
.baseUrl(baseUrl) .baseUrl(baseUrl)
.client(okHttpClient) .client(client)
.addConverterFactory(converterFactory) .addConverterFactory(converterFactory)
.build() .build()
} }

View File

@@ -13,21 +13,28 @@ class RetrofitServiceFactory<T>(
private val baseURLStorage: BaseURLStorage, private val baseURLStorage: BaseURLStorage,
) : ServiceFactory<T> { ) : 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}") Timber.v("provideService() called with: baseUrl = $baseUrl, class = ${serviceClass.simpleName}")
val url = baseUrl ?: baseURLStorage.requireBaseURL() 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 { }.getOrElse {
Timber.e(it, "provideService: can't provide service for $baseUrl") Timber.e(it, "provideService: can't provide service for $baseUrl")
throw NetworkError.MalformedUrl(it) throw NetworkError.MalformedUrl(it)
} }
private fun createService(url: String, serviceClass: Class<T>): T { private fun createService(serviceParams: ServiceParams, serviceClass: Class<T>): T {
Timber.v("createService() called with: url = $url, serviceClass = ${serviceClass.simpleName}") Timber.v("createService() called with: serviceParams = $serviceParams, serviceClass = ${serviceClass.simpleName}")
val service = retrofitBuilder.buildRetrofit(url).create(serviceClass) val (url, needAuth) = serviceParams
cache[url] = service val service = retrofitBuilder.buildRetrofit(url, needAuth).create(serviceClass)
cache[serviceParams] = service
return 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> { 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.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent 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 kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import javax.inject.Named
import javax.inject.Singleton import javax.inject.Singleton
const val AUTH_OK_HTTP = "auth"
const val NO_AUTH_OK_HTTP = "noauth"
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
object NetworkModule { object NetworkModule {
@Provides @Provides
@Singleton @Singleton
fun createOkHttp(okHttpBuilder: OkHttpBuilder): OkHttpClient = @Named(AUTH_OK_HTTP)
okHttpBuilder.buildOkHttp() 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 @Provides
@Singleton @Singleton

View File

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