Simplify error handling

This commit is contained in:
Kirill Kamakin
2022-04-05 15:32:57 +05:00
parent 21feea145a
commit eca325ebe4
11 changed files with 61 additions and 52 deletions

View File

@@ -2,11 +2,12 @@ package gq.kirmanak.mealient.data.auth.impl
import gq.kirmanak.mealient.data.auth.AuthDataSource
import gq.kirmanak.mealient.data.network.ErrorDetail
import gq.kirmanak.mealient.data.network.NetworkError.*
import gq.kirmanak.mealient.data.network.NetworkError.NotMealie
import gq.kirmanak.mealient.data.network.NetworkError.Unauthorized
import gq.kirmanak.mealient.data.network.ServiceFactory
import gq.kirmanak.mealient.extensions.decodeErrorBodyOrNull
import kotlinx.coroutines.CancellationException
import kotlinx.serialization.SerializationException
import gq.kirmanak.mealient.extensions.mapToNetworkError
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
import kotlinx.serialization.json.Json
import retrofit2.HttpException
import retrofit2.Response
@@ -22,12 +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 = try {
authServiceFactory.provideService()
} catch (e: Exception) {
Timber.e(e, "authenticate: can't create Retrofit service")
throw MalformedUrl(e)
}
val authService = authServiceFactory.provideService()
val response = sendRequest(authService, username, password)
val accessToken = parseToken(response)
Timber.v("authenticate() returned: $accessToken")
@@ -38,14 +34,11 @@ class AuthDataSourceImpl @Inject constructor(
authService: AuthService,
username: String,
password: String
): Response<GetTokenResponse> = try {
): Response<GetTokenResponse> = runCatchingExceptCancel {
authService.getToken(username, password)
} catch (e: Throwable) {
throw when (e) {
is CancellationException -> e
is SerializationException -> NotMealie(e)
else -> NoServerConnection(e)
}
}.getOrElse {
Timber.e(it, "sendRequest: can't request token")
throw it.mapToNetworkError()
}
private fun parseToken(

View File

@@ -1,10 +1,9 @@
package gq.kirmanak.mealient.data.baseurl
import gq.kirmanak.mealient.data.network.NetworkError
import gq.kirmanak.mealient.data.network.ServiceFactory
import gq.kirmanak.mealient.extensions.mapToNetworkError
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
import gq.kirmanak.mealient.extensions.versionInfo
import kotlinx.serialization.SerializationException
import retrofit2.HttpException
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -17,21 +16,12 @@ class VersionDataSourceImpl @Inject constructor(
override suspend fun getVersionInfo(baseUrl: String): VersionInfo {
Timber.v("getVersionInfo() called with: baseUrl = $baseUrl")
val service = try {
serviceFactory.provideService(baseUrl)
} catch (e: Exception) {
Timber.e(e, "getVersionInfo: can't create service")
throw NetworkError.MalformedUrl(e)
}
val response = try {
val service = serviceFactory.provideService(baseUrl)
val response = runCatchingExceptCancel {
service.getVersion()
} catch (e: Exception) {
Timber.e(e, "getVersionInfo: can't request version")
when (e) {
is HttpException, is SerializationException -> throw NetworkError.NotMealie(e)
else -> throw NetworkError.NoServerConnection(e)
}
}.getOrElse {
Timber.e(it, "getVersionInfo: can't request version")
throw it.mapToNetworkError()
}
return response.versionInfo()

View File

@@ -1,6 +1,7 @@
package gq.kirmanak.mealient.data.network
import gq.kirmanak.mealient.data.baseurl.BaseURLStorage
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
import timber.log.Timber
inline fun <reified T> RetrofitBuilder.createServiceFactory(baseURLStorage: BaseURLStorage) =
@@ -14,10 +15,13 @@ class RetrofitServiceFactory<T>(
private val cache: MutableMap<String, T> = mutableMapOf()
override suspend fun provideService(baseUrl: String?): T {
override suspend fun provideService(baseUrl: String?): T = runCatchingExceptCancel {
Timber.v("provideService() called with: baseUrl = $baseUrl, class = ${serviceClass.simpleName}")
val url = baseUrl ?: baseURLStorage.requireBaseURL()
return synchronized(cache) { cache[url] ?: createService(url, serviceClass) }
synchronized(cache) { cache[url] ?: createService(url, 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 {

View File

@@ -8,7 +8,7 @@ import gq.kirmanak.mealient.data.recipes.RecipeRepo
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
import gq.kirmanak.mealient.data.recipes.db.entity.RecipeSummaryEntity
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
import kotlinx.coroutines.CancellationException
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -39,12 +39,10 @@ class RecipeRepoImpl @Inject constructor(
override suspend fun loadRecipeInfo(recipeId: Long, recipeSlug: String): FullRecipeInfo {
Timber.v("loadRecipeInfo() called with: recipeId = $recipeId, recipeSlug = $recipeSlug")
try {
runCatchingExceptCancel {
storage.saveRecipeInfo(dataSource.requestRecipeInfo(recipeSlug))
} catch (e: CancellationException) {
throw e
} catch (e: Throwable) {
Timber.e(e, "loadRecipeInfo: can't update full recipe info")
}.onFailure {
Timber.e(it, "loadRecipeInfo: can't update full recipe info")
}
return storage.queryRecipeInfo(recipeId)

View File

@@ -7,7 +7,7 @@ import androidx.paging.LoadType.REFRESH
import gq.kirmanak.mealient.data.recipes.db.RecipeStorage
import gq.kirmanak.mealient.data.recipes.db.entity.RecipeSummaryEntity
import gq.kirmanak.mealient.data.recipes.network.RecipeDataSource
import kotlinx.coroutines.CancellationException
import gq.kirmanak.mealient.extensions.runCatchingExceptCancel
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -37,16 +37,14 @@ class RecipesRemoteMediator @Inject constructor(
val start = if (loadType == REFRESH) 0 else lastRequestEnd
val limit = if (loadType == REFRESH) state.config.initialLoadSize else state.config.pageSize
val count: Int = try {
val count: Int = runCatchingExceptCancel {
val recipes = network.requestRecipes(start, limit)
if (loadType == REFRESH) storage.refreshAll(recipes)
else storage.saveRecipes(recipes)
recipes.size
} catch (e: CancellationException) {
throw e
} catch (e: Throwable) {
Timber.e(e, "Can't load recipes")
return MediatorResult.Error(e)
}.getOrElse {
Timber.e(it, "load: can't load recipes")
return MediatorResult.Error(it)
}
// After something is inserted into DB the paging sources have to be invalidated