diff --git a/app/src/main/java/gq/kirmanak/mealient/data/AppDb.kt b/app/src/main/java/gq/kirmanak/mealient/data/AppDb.kt index 67c20d2..b2d4da5 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/AppDb.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/AppDb.kt @@ -6,7 +6,6 @@ import androidx.room.TypeConverters import gq.kirmanak.mealient.data.impl.RoomTypeConverters import gq.kirmanak.mealient.data.recipes.db.RecipeDao import gq.kirmanak.mealient.data.recipes.db.entity.* -import javax.inject.Singleton @Database( version = 1, @@ -14,7 +13,6 @@ import javax.inject.Singleton exportSchema = false ) @TypeConverters(RoomTypeConverters::class) -@Singleton abstract class AppDb : RoomDatabase() { abstract fun recipeDao(): RecipeDao } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/AppModule.kt b/app/src/main/java/gq/kirmanak/mealient/data/AppModule.kt index 672fa40..294e03c 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/AppModule.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/AppModule.kt @@ -9,19 +9,35 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import gq.kirmanak.mealient.data.impl.OkHttpBuilder +import kotlinx.serialization.json.Json +import okhttp3.OkHttpClient +import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) interface AppModule { companion object { @Provides - fun createDb(@ApplicationContext context: Context): AppDb { - return Room.databaseBuilder(context, AppDb::class.java, "app.db").build() - } + @Singleton + fun createDb(@ApplicationContext context: Context): AppDb = + Room.databaseBuilder(context, AppDb::class.java, "app.db").build() @Provides - fun createSharedPreferences(@ApplicationContext context: Context): SharedPreferences { - return PreferenceManager.getDefaultSharedPreferences(context) + @Singleton + fun createSharedPreferences(@ApplicationContext context: Context): SharedPreferences = + PreferenceManager.getDefaultSharedPreferences(context) + + @Provides + @Singleton + fun createOkHttp(okHttpBuilder: OkHttpBuilder): OkHttpClient = + okHttpBuilder.buildOkHttp() + + @Provides + @Singleton + fun createJson(): Json = Json { + coerceInputValues = true + ignoreUnknownKeys = true } } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/auth/impl/AuthOkHttpInterceptor.kt b/app/src/main/java/gq/kirmanak/mealient/data/auth/impl/AuthOkHttpInterceptor.kt index d5c910f..e6f9570 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/auth/impl/AuthOkHttpInterceptor.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/auth/impl/AuthOkHttpInterceptor.kt @@ -1,18 +1,29 @@ package gq.kirmanak.mealient.data.auth.impl +import gq.kirmanak.mealient.data.auth.AuthStorage +import kotlinx.coroutines.runBlocking import okhttp3.Interceptor import okhttp3.Response +import timber.log.Timber +import javax.inject.Inject const val AUTHORIZATION_HEADER = "Authorization" -class AuthOkHttpInterceptor(token: String) : Interceptor { - private val headerValue = "Bearer $token" - +class AuthOkHttpInterceptor @Inject constructor( + private val authStorage: AuthStorage +) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { - val newRequest = chain.request() - .newBuilder() - .addHeader(AUTHORIZATION_HEADER, headerValue) - .build() - return chain.proceed(newRequest) + Timber.v("intercept() called with: chain = $chain") + val token = runBlocking { authStorage.getToken() } + Timber.d("intercept: token = $token") + val request = if (token.isNullOrBlank()) { + chain.request() + } else { + chain.request() + .newBuilder() + .addHeader(AUTHORIZATION_HEADER, "Bearer $token") + .build() + } + return chain.proceed(request) } } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt b/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt index a0b5805..5e05065 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/impl/OkHttpBuilder.kt @@ -8,12 +8,14 @@ import okhttp3.logging.HttpLoggingInterceptor import timber.log.Timber import javax.inject.Inject -class OkHttpBuilder @Inject constructor() { - fun buildOkHttp(token: String?): OkHttpClient { - Timber.v("buildOkHttp() called with: token = $token") +class OkHttpBuilder @Inject constructor( + private val authOkHttpInterceptor: AuthOkHttpInterceptor +) { + fun buildOkHttp(): OkHttpClient { + Timber.v("buildOkHttp() called") val builder = OkHttpClient.Builder() if (BuildConfig.DEBUG) builder.addNetworkInterceptor(buildLoggingInterceptor()) - if (token != null) builder.addNetworkInterceptor(AuthOkHttpInterceptor(token)) + builder.addNetworkInterceptor(authOkHttpInterceptor) return builder.build() } diff --git a/app/src/main/java/gq/kirmanak/mealient/data/impl/RetrofitBuilder.kt b/app/src/main/java/gq/kirmanak/mealient/data/impl/RetrofitBuilder.kt index e408ae3..b8fb26e 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/impl/RetrofitBuilder.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/impl/RetrofitBuilder.kt @@ -4,26 +4,23 @@ import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFact import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import okhttp3.MediaType.Companion.toMediaType +import okhttp3.OkHttpClient import retrofit2.Retrofit import timber.log.Timber import javax.inject.Inject @ExperimentalSerializationApi -class RetrofitBuilder @Inject constructor(private val okHttpBuilder: OkHttpBuilder) { - private val json by lazy { - Json { - coerceInputValues = true - ignoreUnknownKeys = true - } - } - - fun buildRetrofit(baseUrl: String, token: String? = null): Retrofit { - Timber.v("buildRetrofit() called with: baseUrl = $baseUrl, token = $token") +class RetrofitBuilder @Inject constructor( + private val okHttpClient: OkHttpClient, + private val json: Json +) { + fun buildRetrofit(baseUrl: String): Retrofit { + Timber.v("buildRetrofit() called with: baseUrl = $baseUrl") val contentType = "application/json".toMediaType() val converterFactory = json.asConverterFactory(contentType) return Retrofit.Builder() .baseUrl(baseUrl) - .client(okHttpBuilder.buildOkHttp(token)) + .client(okHttpClient) .addConverterFactory(converterFactory) .build() } diff --git a/app/src/main/java/gq/kirmanak/mealient/data/recipes/network/RecipeDataSourceImpl.kt b/app/src/main/java/gq/kirmanak/mealient/data/recipes/network/RecipeDataSourceImpl.kt index e35e377..d90efd0 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/recipes/network/RecipeDataSourceImpl.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/recipes/network/RecipeDataSourceImpl.kt @@ -38,7 +38,7 @@ class RecipeDataSourceImpl @Inject constructor( val baseUrl = checkNotNull(authRepo.getBaseUrl()) { "Base url is null" } val token = checkNotNull(authRepo.getToken()) { "Token is null" } Timber.d("requestRecipes: baseUrl = $baseUrl, token = $token") - val retrofit = retrofitBuilder.buildRetrofit(baseUrl, token) + val retrofit = retrofitBuilder.buildRetrofit(baseUrl) val createdService = retrofit.create(RecipeService::class.java) _recipeService = createdService createdService diff --git a/app/src/test/java/gq/kirmanak/mealient/data/impl/OkHttpBuilderTest.kt b/app/src/test/java/gq/kirmanak/mealient/data/impl/OkHttpBuilderTest.kt index 4be5e63..87d795d 100644 --- a/app/src/test/java/gq/kirmanak/mealient/data/impl/OkHttpBuilderTest.kt +++ b/app/src/test/java/gq/kirmanak/mealient/data/impl/OkHttpBuilderTest.kt @@ -2,8 +2,10 @@ package gq.kirmanak.mealient.data.impl import com.google.common.truth.Truth.assertThat import dagger.hilt.android.testing.HiltAndroidTest +import gq.kirmanak.mealient.data.auth.AuthStorage import gq.kirmanak.mealient.data.auth.impl.AUTHORIZATION_HEADER import gq.kirmanak.mealient.test.AuthImplTestData.TEST_TOKEN +import gq.kirmanak.mealient.test.AuthImplTestData.TEST_URL import gq.kirmanak.mealient.test.MockServerTest import okhttp3.OkHttpClient import okhttp3.Request @@ -17,16 +19,20 @@ class OkHttpBuilderTest : MockServerTest() { @Inject lateinit var subject: OkHttpBuilder + @Inject + lateinit var authStorage: AuthStorage + @Test fun `when token null then no auth header`() { - val client = subject.buildOkHttp(null) + val client = subject.buildOkHttp() val header = sendRequestAndExtractAuthHeader(client) assertThat(header).isNull() } @Test fun `when token isn't null then auth header contains token`() { - val client = subject.buildOkHttp(TEST_TOKEN) + authStorage.storeAuthData(TEST_TOKEN, TEST_URL) + val client = subject.buildOkHttp() val header = sendRequestAndExtractAuthHeader(client) assertThat(header).isEqualTo("Bearer $TEST_TOKEN") }