Simplify logAndMapErrors calls

This commit is contained in:
Kirill Kamakin
2022-08-05 21:54:52 +02:00
parent 5297419b33
commit 81e06dc1d8
4 changed files with 17 additions and 23 deletions

View File

@@ -17,8 +17,7 @@ class AddRecipeDataSourceImpl @Inject constructor(
override suspend fun addRecipe(recipe: AddRecipeRequest): String { override suspend fun addRecipe(recipe: AddRecipeRequest): String {
logger.v { "addRecipe() called with: recipe = $recipe" } logger.v { "addRecipe() called with: recipe = $recipe" }
val service = addRecipeServiceFactory.provideService() val service = addRecipeServiceFactory.provideService()
val response = logAndMapErrors( val response = logger.logAndMapErrors(
logger,
block = { service.addRecipe(recipe) }, block = { service.addRecipe(recipe) },
logProvider = { "addRecipe: can't add recipe" } logProvider = { "addRecipe: can't add recipe" }
) )

View File

@@ -5,7 +5,7 @@ import gq.kirmanak.mealient.data.network.ErrorDetail
import gq.kirmanak.mealient.data.network.NetworkError.NotMealie import gq.kirmanak.mealient.data.network.NetworkError.NotMealie
import gq.kirmanak.mealient.data.network.NetworkError.Unauthorized import gq.kirmanak.mealient.data.network.NetworkError.Unauthorized
import gq.kirmanak.mealient.data.network.ServiceFactory import gq.kirmanak.mealient.data.network.ServiceFactory
import gq.kirmanak.mealient.extensions.decodeErrorBodyOrNull import gq.kirmanak.mealient.extensions.decodeErrorBody
import gq.kirmanak.mealient.extensions.logAndMapErrors import gq.kirmanak.mealient.extensions.logAndMapErrors
import gq.kirmanak.mealient.logging.Logger import gq.kirmanak.mealient.logging.Logger
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@@ -34,8 +34,7 @@ class AuthDataSourceImpl @Inject constructor(
authService: AuthService, authService: AuthService,
username: String, username: String,
password: String password: String
): Response<GetTokenResponse> = logAndMapErrors( ): Response<GetTokenResponse> = logger.logAndMapErrors(
logger,
block = { authService.getToken(username = username, password = password) }, block = { authService.getToken(username = username, password = password) },
logProvider = { "sendRequest: can't get token" }, logProvider = { "sendRequest: can't get token" },
) )
@@ -46,7 +45,9 @@ class AuthDataSourceImpl @Inject constructor(
response.body()?.accessToken ?: throw NotMealie(NullPointerException("Body is null")) response.body()?.accessToken ?: throw NotMealie(NullPointerException("Body is null"))
} else { } else {
val cause = HttpException(response) val cause = HttpException(response)
val errorDetail: ErrorDetail? = response.decodeErrorBodyOrNull(json, logger) val errorDetail = json.runCatching<Json, ErrorDetail> { decodeErrorBody(response) }
.onFailure { logger.e(it) { "Can't decode error body" } }
.getOrNull()
throw when (errorDetail?.detail) { throw when (errorDetail?.detail) {
"Unauthorized" -> Unauthorized(cause) "Unauthorized" -> Unauthorized(cause)
else -> NotMealie(cause) else -> NotMealie(cause)

View File

@@ -19,8 +19,7 @@ class VersionDataSourceImpl @Inject constructor(
logger.v { "getVersionInfo() called with: baseUrl = $baseUrl" } logger.v { "getVersionInfo() called with: baseUrl = $baseUrl" }
val service = serviceFactory.provideService(baseUrl) val service = serviceFactory.provideService(baseUrl)
val response = logAndMapErrors( val response = logger.logAndMapErrors(
logger,
block = { service.getVersion() }, block = { service.getVersion() },
logProvider = { "getVersionInfo: can't request version" } logProvider = { "getVersionInfo: can't request version" }
) )

View File

@@ -8,28 +8,23 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import retrofit2.HttpException import retrofit2.HttpException
import retrofit2.Response import retrofit2.Response
import java.io.InputStream
inline fun <T, reified R> Response<T>.decodeErrorBodyOrNull(json: Json, logger: Logger): R? =
errorBody()?.byteStream()?.let { json.decodeFromStreamOrNull<R>(it, logger) }
@OptIn(ExperimentalSerializationApi::class) @OptIn(ExperimentalSerializationApi::class)
inline fun <reified T> Json.decodeFromStreamOrNull(stream: InputStream, logger: Logger): T? = inline fun <T, reified R> Json.decodeErrorBody(response: Response<T>): R =
runCatching { decodeFromStream<T>(stream) } checkNotNull(response.errorBody()) { "Can't decode absent error body" }
.onFailure { logger.e(it) { "decodeFromStreamOrNull: can't decode" } } .byteStream()
.getOrNull() .let(::decodeFromStream)
fun Throwable.mapToNetworkError(): NetworkError = when (this) { fun Throwable.mapToNetworkError(): NetworkError = when (this) {
is HttpException, is SerializationException -> NetworkError.NotMealie(this) is HttpException, is SerializationException -> NetworkError.NotMealie(this)
else -> NetworkError.NoServerConnection(this) else -> NetworkError.NoServerConnection(this)
} }
inline fun <T> logAndMapErrors( inline fun <T> Logger.logAndMapErrors(
logger: Logger,
block: () -> T, block: () -> T,
noinline logProvider: () -> String noinline logProvider: () -> String
): T = ): T = runCatchingExceptCancel(block).getOrElse {
runCatchingExceptCancel(block).getOrElse { e(it, messageSupplier = logProvider)
logger.e(it, messageSupplier = logProvider)
throw it.mapToNetworkError() throw it.mapToNetworkError()
} }