I just got a problem to inject repository in the interceptor to get access token when its needed or rather expired. I just don't understand where I did something wrong. I just didn't find any example how to deal with interceptor and repository. Thinking about that double "Retrofit.Builder" maybe this is a problem. What do you think? And let's the code talk:
@Module
class AppModule {
@Singleton
@Provides
fun provideRefreshTokenService(client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
@Singleton
@Provides
fun provideHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addNetworkInterceptor(headerInterceptor)
.build()
}
@Singleton
@Provides
fun provideRetrofit(client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}
class TwichHeaderRepository @Inject constructor(
private val refreshTokenApi: RefreshTokenApi)
{
suspend fun fetchRefreshToken(): Response<RefreshToken> {
return withContext(Dispatchers.IO) {
refreshTokenApi.getRefreshToken()
}
}
}
private const val TAG = "AddRepositoryAction"
private const val HEADER_CLIENT_ID = "Client-ID"
private const val HEADER_AUTHORIZATION = "Authorization"
private const val HEADER_ACCEPT = "Accept"
private const val DEFAULT_ACCESS_TOKEN = "mjycvndz4sasons2mg990kqme6vu6d"
private const val UNAUTHORIZED_STATUS_CODE = 401
@Singleton
class HeaderInterceptor @Inject constructor(
private val context: Context,
private val twichHeaderRepository: TwichHeaderRepository
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $DEFAULT_ACCESS_TOKEN")
header(HEADER_ACCEPT, "application/json")
}.build())
val refreshToken = runBlocking { generateAccessToken(twichHeaderRepository) }
if (response.code == UNAUTHORIZED_STATUS_CODE) {
response.close()
val accessToken = ""
return chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $accessToken")
header(HEADER_ACCEPT, "application/json")
}.build())
}
return response
}
}
private suspend fun generateAccessToken(twichHeaderRepository: TwichHeaderRepository): String
{
val responseRefreshToken = twichHeaderRepository.fetchRefreshToken()
return responseRefreshToken.body().toString()
}
interface RefreshTokenApi {
@POST(
...
)
suspend fun getRefreshToken(
): Response<RefreshToken>
}
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context): AppComponent
}
fun inject(activity: MainActivity)
fun inject(fragment: AddFragment)
fun inject(interceptor: HeaderInterceptor)
}
class TwichRepository @Inject constructor(
private val twichApi: TwichApi
) {
suspend fun searchGames(title: String): Response<Game> {
return withContext(Dispatchers.IO) { twichApi.getGamesBySearch(title) }
}
}
Error message is
Found a dependency cycle okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRefreshTokenService(client)
com.example.glc.add.RefreshTokenApi is injected at
com.example.glc.add.TwichHeaderRepository(refreshTokenApi)
com.example.glc.add.TwichHeaderRepository is injected at
com.example.glc.add.HeaderInterceptor(�, twichHeaderRepository)
com.example.glc.add.HeaderInterceptor is injected at
com.example.glc.AppModule.provideHttpClient(headerInterceptor)
okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRetrofit(client)
com.example.glc.add.TwichApi is injected at
com.example.glc.add.TwichRepository(twichApi)
com.example.glc.add.TwichRepository is injected at
com.example.glc.add.AddViewModel(twichRepository)
com.example.glc.add.AddViewModel is injected at
com.example.glc.add.AddFragment.addViewModel
com.example.glc.add.AddFragment is injected at
com.example.glc.di.AppComponent.inject(com.example.glc.add.AddFragment)
I just needed to add @Named
to resolve my problem and understand it as Mark said in the comment.
@Module
class AppModule {
@Singleton
@Provides
@Named("oauth2")
fun provideAccessTokenHttpClient() = OkHttpClient.Builder().build()
@Singleton
@Provides
fun provideRefreshTokenService(@Named("oauth2") client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
@Singleton
@Provides
@Named("igdb")
fun provideSearchDataHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(headerInterceptor)
.build()
}
@Singleton
@Provides
fun provideRetrofit(@Named("igdb") client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}