Skip to content

Commit

Permalink
Add networking classes to provide a configured Retrofit instance
Browse files Browse the repository at this point in the history
  • Loading branch information
michpohl committed Oct 24, 2024
1 parent 545614e commit d98124c
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.tidal.sdk.tidalapi.networking

import com.tidal.sdk.auth.CredentialsProvider
import kotlinx.coroutines.runBlocking
import okhttp3.Interceptor
import okhttp3.Response

/**
* An [Interceptor] that adds an Authorization header to each request.
*
* @param[credentialsProvider] A [CredentialsProvider] that provides the access token used in the
* Authorization header.
*/
internal class AuthInterceptor(private val credentialsProvider: CredentialsProvider) :
Interceptor {

@Suppress("ReturnCount")
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val credentials = runBlocking { credentialsProvider.getCredentials().successData }
?: return chain.proceed(request)
return chain.proceed(
request.newBuilder().header("Authorization", "Bearer ${credentials.token}").build(),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.tidal.sdk.tidalapi.networking

import com.tidal.sdk.auth.CredentialsProvider
import kotlinx.coroutines.runBlocking
import okhttp3.Authenticator
import okhttp3.Request
import okhttp3.Response
import okhttp3.Route

/**
* Implementation of [Authenticator] that will delegate decision of authenticating to the provided
* [CredentialsProvider].
* @param[credentialsProvider] A [CredentialsProvider] that provides the access token used in the
* Authorization header of the request.
*/
internal class DefaultAuthenticator(private val credentialsProvider: CredentialsProvider) :
Authenticator {

override fun authenticate(route: Route?, response: Response): Request? = runBlocking {
credentialsProvider.getCredentials()
}.successData?.let {
response.request.newBuilder().header("Authorization", "Bearer ${it.token}").build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.tidal.sdk.tidalapi.networking

import com.tidal.sdk.auth.CredentialsProvider
import com.tidal.sdk.common.d
import com.tidal.sdk.common.logger
import com.tidal.sdk.tidalapi.generated.models.getOneOfSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.modules.SerializersModule
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Converter
import retrofit2.Retrofit
import retrofit2.converter.kotlinx.serialization.asConverterFactory
import retrofit2.converter.scalars.ScalarsConverterFactory

class RetrofitProvider {

private val converterFactories: List<Converter.Factory> = listOf(
ScalarsConverterFactory.create(),
createJsonSerializer().asConverterFactory("application/json".toMediaType()),
)

private fun provideOkHttpClientBuilder(credentialsProvider: CredentialsProvider): OkHttpClient =
OkHttpClient.Builder().addInterceptor(
AuthInterceptor(credentialsProvider),
)
.authenticator(DefaultAuthenticator(credentialsProvider))
.addInterceptor(getLoggingInterceptor()).build()

fun provideRetrofit(baseUrl: String, credentialsProvider: CredentialsProvider): Retrofit =
Retrofit.Builder().baseUrl(
baseUrl,
).client(
provideOkHttpClientBuilder(credentialsProvider),
)
.apply {
converterFactories.forEach {
addConverterFactory(it)
}
}.build()

private fun getLoggingInterceptor(): HttpLoggingInterceptor {
val logging = HttpLoggingInterceptor.Logger { String.logger.d { it } }
return HttpLoggingInterceptor(logging).apply {
setLevel(HttpLoggingInterceptor.Level.BODY)
}
}

private fun createJsonSerializer(): Json {
val oneOfSerializer = SerializersModule {
include(getOneOfSerializer())
}

return Json {
classDiscriminator = "type"
ignoreUnknownKeys = true
serializersModule = oneOfSerializer
}
}
}

0 comments on commit d98124c

Please sign in to comment.