Extract duplicated makeCall methods
This commit is contained in:
@@ -79,4 +79,8 @@ interface DataSourceModule {
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindMealieDataSourceV1(mealientDataSourceImpl: MealieDataSourceV1Impl): MealieDataSourceV1
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindNetworkRequestWrapper(networkRequestWrapperImpl: NetworkRequestWrapperImpl): NetworkRequestWrapper
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package gq.kirmanak.mealient.datasource
|
||||
|
||||
interface NetworkRequestWrapper {
|
||||
|
||||
suspend fun <T> makeCall(
|
||||
block: suspend () -> T,
|
||||
logMethod: () -> String,
|
||||
logParameters: () -> String,
|
||||
): Result<T>
|
||||
|
||||
suspend fun <T> makeCallAndHandleUnauthorized(
|
||||
block: suspend () -> T,
|
||||
logMethod: () -> String,
|
||||
logParameters: () -> String,
|
||||
): T
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package gq.kirmanak.mealient.datasource
|
||||
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import retrofit2.HttpException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NetworkRequestWrapperImpl @Inject constructor(
|
||||
private val logger: Logger,
|
||||
) : NetworkRequestWrapper {
|
||||
|
||||
override suspend fun <T> makeCall(
|
||||
block: suspend () -> T,
|
||||
logMethod: () -> String,
|
||||
logParameters: () -> String,
|
||||
): Result<T> {
|
||||
logger.v { "${logMethod()} called with: ${logParameters()}" }
|
||||
return runCatchingExceptCancel { block() }
|
||||
.onFailure { logger.e(it) { "${logMethod()} request failed with: ${logParameters()}" } }
|
||||
.onSuccess { logger.d { "${logMethod()} request succeeded with ${logParameters()}, result = $it" } }
|
||||
}
|
||||
|
||||
override suspend fun <T> makeCallAndHandleUnauthorized(
|
||||
block: suspend () -> T,
|
||||
logMethod: () -> String,
|
||||
logParameters: () -> String
|
||||
): T = makeCall(block, logMethod, logParameters).getOrElse {
|
||||
throw if (it is HttpException && it.code() in listOf(401, 403)) {
|
||||
NetworkError.Unauthorized(it)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package gq.kirmanak.mealient.datasource.v0
|
||||
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.datasource.NetworkRequestWrapper
|
||||
import gq.kirmanak.mealient.datasource.v0.models.*
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
@@ -17,23 +17,28 @@ import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class MealieDataSourceV0Impl @Inject constructor(
|
||||
private val networkRequestWrapper: NetworkRequestWrapper,
|
||||
private val logger: Logger,
|
||||
private val mealieServiceV0: MealieServiceV0,
|
||||
private val service: MealieServiceV0,
|
||||
private val json: Json,
|
||||
) : MealieDataSourceV0 {
|
||||
|
||||
override suspend fun addRecipe(
|
||||
baseUrl: String, token: String?, recipe: AddRecipeRequestV0
|
||||
): String = makeCall(
|
||||
block = { addRecipe("$baseUrl/api/recipes/create", token, recipe) },
|
||||
baseUrl: String,
|
||||
token: String?,
|
||||
recipe: AddRecipeRequestV0,
|
||||
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.addRecipe("$baseUrl/api/recipes/create", token, recipe) },
|
||||
logMethod = { "addRecipe" },
|
||||
logParameters = { "baseUrl = $baseUrl, token = $token, recipe = $recipe" }
|
||||
).getOrThrowUnauthorized()
|
||||
)
|
||||
|
||||
override suspend fun authenticate(
|
||||
baseUrl: String, username: String, password: String
|
||||
): String = makeCall(
|
||||
block = { getToken("$baseUrl/api/auth/token", username, password) },
|
||||
baseUrl: String,
|
||||
username: String,
|
||||
password: String,
|
||||
): String = networkRequestWrapper.makeCall(
|
||||
block = { service.getToken("$baseUrl/api/auth/token", username, password) },
|
||||
logMethod = { "authenticate" },
|
||||
logParameters = { "baseUrl = $baseUrl, username = $username, password = $password" }
|
||||
).map { it.accessToken }.getOrElse {
|
||||
@@ -42,58 +47,41 @@ class MealieDataSourceV0Impl @Inject constructor(
|
||||
throw if (errorDetailV0.detail == "Unauthorized") NetworkError.Unauthorized(it) else it
|
||||
}
|
||||
|
||||
override suspend fun getVersionInfo(baseUrl: String): VersionResponseV0 = makeCall(
|
||||
block = { getVersion("$baseUrl/api/debug/version") },
|
||||
override suspend fun getVersionInfo(
|
||||
baseUrl: String
|
||||
): VersionResponseV0 = networkRequestWrapper.makeCall(
|
||||
block = { service.getVersion("$baseUrl/api/debug/version") },
|
||||
logMethod = { "getVersionInfo" },
|
||||
logParameters = { "baseUrl = $baseUrl" },
|
||||
).getOrElse {
|
||||
when (it) {
|
||||
is HttpException, is SerializationException -> throw NetworkError.NotMealie(it)
|
||||
is SocketTimeoutException, is ConnectException -> throw NetworkError.NoServerConnection(
|
||||
it
|
||||
)
|
||||
else -> throw NetworkError.MalformedUrl(it)
|
||||
throw when (it) {
|
||||
is HttpException, is SerializationException -> NetworkError.NotMealie(it)
|
||||
is SocketTimeoutException, is ConnectException -> NetworkError.NoServerConnection(it)
|
||||
else -> NetworkError.MalformedUrl(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun requestRecipes(
|
||||
baseUrl: String, token: String?, start: Int, limit: Int
|
||||
): List<GetRecipeSummaryResponseV0> = makeCall(
|
||||
block = { getRecipeSummary("$baseUrl/api/recipes/summary", token, start, limit) },
|
||||
baseUrl: String,
|
||||
token: String?,
|
||||
start: Int,
|
||||
limit: Int,
|
||||
): List<GetRecipeSummaryResponseV0> = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getRecipeSummary("$baseUrl/api/recipes/summary", token, start, limit) },
|
||||
logMethod = { "requestRecipes" },
|
||||
logParameters = { "baseUrl = $baseUrl, token = $token, start = $start, limit = $limit" }
|
||||
).getOrElse {
|
||||
val code = (it as? HttpException)?.code() ?: throw it
|
||||
if (code == 404) throw NetworkError.NotMealie(it) else throw it
|
||||
}
|
||||
)
|
||||
|
||||
override suspend fun requestRecipeInfo(
|
||||
baseUrl: String, token: String?, slug: String
|
||||
): GetRecipeResponseV0 = makeCall(
|
||||
block = { getRecipe("$baseUrl/api/recipes/$slug", token) },
|
||||
baseUrl: String,
|
||||
token: String?,
|
||||
slug: String,
|
||||
): GetRecipeResponseV0 = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getRecipe("$baseUrl/api/recipes/$slug", token) },
|
||||
logMethod = { "requestRecipeInfo" },
|
||||
logParameters = { "baseUrl = $baseUrl, token = $token, slug = $slug" }
|
||||
).getOrThrowUnauthorized()
|
||||
|
||||
private suspend inline fun <T> makeCall(
|
||||
crossinline block: suspend MealieServiceV0.() -> T,
|
||||
crossinline logMethod: () -> String,
|
||||
crossinline logParameters: () -> String,
|
||||
): Result<T> {
|
||||
logger.v { "${logMethod()} called with: ${logParameters()}" }
|
||||
return runCatchingExceptCancel { mealieServiceV0.block() }
|
||||
.onFailure { logger.e(it) { "${logMethod()} request failed with: ${logParameters()}" } }
|
||||
.onSuccess { logger.d { "${logMethod()} request succeeded with ${logParameters()}" } }
|
||||
}
|
||||
)
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
private inline fun <reified R> ResponseBody.decode(): R = json.decodeFromStream(byteStream())
|
||||
}
|
||||
|
||||
private fun <T> Result<T>.getOrThrowUnauthorized(): T = getOrElse {
|
||||
throw if (it is HttpException && it.code() in listOf(401, 403)) {
|
||||
NetworkError.Unauthorized(it)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
package gq.kirmanak.mealient.datasource.v1
|
||||
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.datasource.runCatchingExceptCancel
|
||||
import gq.kirmanak.mealient.datasource.NetworkRequestWrapper
|
||||
import gq.kirmanak.mealient.datasource.v0.models.AddRecipeRequestV0
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.VersionResponseV1
|
||||
import gq.kirmanak.mealient.logging.Logger
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.Json
|
||||
import retrofit2.HttpException
|
||||
@@ -17,8 +16,8 @@ import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class MealieDataSourceV1Impl @Inject constructor(
|
||||
private val logger: Logger,
|
||||
private val mealieService: MealieServiceV1,
|
||||
private val networkRequestWrapper: NetworkRequestWrapper,
|
||||
private val service: MealieServiceV1,
|
||||
private val json: Json,
|
||||
) : MealieDataSourceV1 {
|
||||
|
||||
@@ -34,8 +33,10 @@ class MealieDataSourceV1Impl @Inject constructor(
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override suspend fun getVersionInfo(baseUrl: String): VersionResponseV1 = makeCall(
|
||||
block = { getVersion("$baseUrl/api/app/about") },
|
||||
override suspend fun getVersionInfo(
|
||||
baseUrl: String,
|
||||
): VersionResponseV1 = networkRequestWrapper.makeCall(
|
||||
block = { service.getVersion("$baseUrl/api/app/about") },
|
||||
logMethod = { "getVersionInfo" },
|
||||
logParameters = { "baseUrl = $baseUrl" },
|
||||
).getOrElse {
|
||||
@@ -51,38 +52,21 @@ class MealieDataSourceV1Impl @Inject constructor(
|
||||
token: String?,
|
||||
page: Int,
|
||||
perPage: Int
|
||||
): List<GetRecipeSummaryResponseV1> = makeCall(
|
||||
block = { getRecipeSummary("$baseUrl/api/recipes", token, page, perPage) },
|
||||
): List<GetRecipeSummaryResponseV1> = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getRecipeSummary("$baseUrl/api/recipes", token, page, perPage) },
|
||||
logMethod = { "requestRecipesV1" },
|
||||
logParameters = { "baseUrl = $baseUrl, token = $token, page = $page, perPage = $perPage" }
|
||||
).map { it.items }.getOrThrowUnauthorized()
|
||||
).items
|
||||
|
||||
override suspend fun requestRecipeInfo(
|
||||
baseUrl: String,
|
||||
token: String?,
|
||||
slug: String
|
||||
): GetRecipeResponseV1 = makeCall(
|
||||
block = { getRecipe("$baseUrl/api/recipes/$slug", token) },
|
||||
): GetRecipeResponseV1 = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getRecipe("$baseUrl/api/recipes/$slug", token) },
|
||||
logMethod = { "requestRecipeInfo" },
|
||||
logParameters = { "baseUrl = $baseUrl, token = $token, slug = $slug" }
|
||||
).getOrThrowUnauthorized()
|
||||
)
|
||||
|
||||
private suspend inline fun <T> makeCall(
|
||||
crossinline block: suspend MealieServiceV1.() -> T,
|
||||
crossinline logMethod: () -> String,
|
||||
crossinline logParameters: () -> String,
|
||||
): Result<T> {
|
||||
logger.v { "${logMethod()} called with: ${logParameters()}" }
|
||||
return runCatchingExceptCancel { mealieService.block() }
|
||||
.onFailure { logger.e(it) { "${logMethod()} request failed with: ${logParameters()}" } }
|
||||
.onSuccess { logger.d { "${logMethod()} request succeeded with ${logParameters()}" } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> Result<T>.getOrThrowUnauthorized(): T = getOrElse {
|
||||
throw if (it is HttpException && it.code() in listOf(401, 403)) {
|
||||
NetworkError.Unauthorized(it)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user