From 5d99b0ee964b9e86cd9fd1b1a48bc5ed3006b146 Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Sat, 29 Oct 2022 19:46:09 +0200 Subject: [PATCH] Extract duplicated makeCall methods --- .../mealient/datasource/DataSourceModule.kt | 4 + .../datasource/NetworkRequestWrapper.kt | 17 ++++ .../datasource/NetworkRequestWrapperImpl.kt | 36 ++++++++ .../datasource/v0/MealieDataSourceV0Impl.kt | 82 ++++++++----------- .../datasource/v1/MealieDataSourceV1Impl.kt | 42 +++------- 5 files changed, 105 insertions(+), 76 deletions(-) create mode 100644 datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/NetworkRequestWrapper.kt create mode 100644 datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/NetworkRequestWrapperImpl.kt diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/DataSourceModule.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/DataSourceModule.kt index f737948..afaf344 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/DataSourceModule.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/DataSourceModule.kt @@ -79,4 +79,8 @@ interface DataSourceModule { @Binds @Singleton fun bindMealieDataSourceV1(mealientDataSourceImpl: MealieDataSourceV1Impl): MealieDataSourceV1 + + @Binds + @Singleton + fun bindNetworkRequestWrapper(networkRequestWrapperImpl: NetworkRequestWrapperImpl): NetworkRequestWrapper } \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/NetworkRequestWrapper.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/NetworkRequestWrapper.kt new file mode 100644 index 0000000..8a8dd01 --- /dev/null +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/NetworkRequestWrapper.kt @@ -0,0 +1,17 @@ +package gq.kirmanak.mealient.datasource + +interface NetworkRequestWrapper { + + suspend fun makeCall( + block: suspend () -> T, + logMethod: () -> String, + logParameters: () -> String, + ): Result + + suspend fun makeCallAndHandleUnauthorized( + block: suspend () -> T, + logMethod: () -> String, + logParameters: () -> String, + ): T + +} \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/NetworkRequestWrapperImpl.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/NetworkRequestWrapperImpl.kt new file mode 100644 index 0000000..5bf01dc --- /dev/null +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/NetworkRequestWrapperImpl.kt @@ -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 makeCall( + block: suspend () -> T, + logMethod: () -> String, + logParameters: () -> String, + ): Result { + 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 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 + } + } + +} \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0Impl.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0Impl.kt index 83e75f5..d8fd7b8 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0Impl.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v0/MealieDataSourceV0Impl.kt @@ -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 = makeCall( - block = { getRecipeSummary("$baseUrl/api/recipes/summary", token, start, limit) }, + baseUrl: String, + token: String?, + start: Int, + limit: Int, + ): List = 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 makeCall( - crossinline block: suspend MealieServiceV0.() -> T, - crossinline logMethod: () -> String, - crossinline logParameters: () -> String, - ): Result { - 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 ResponseBody.decode(): R = json.decodeFromStream(byteStream()) } - -private fun Result.getOrThrowUnauthorized(): T = getOrElse { - throw if (it is HttpException && it.code() in listOf(401, 403)) { - NetworkError.Unauthorized(it) - } else { - it - } -} \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt index 17d0615..9a743bb 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt @@ -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 = makeCall( - block = { getRecipeSummary("$baseUrl/api/recipes", token, page, perPage) }, + ): List = 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 makeCall( - crossinline block: suspend MealieServiceV1.() -> T, - crossinline logMethod: () -> String, - crossinline logParameters: () -> String, - ): Result { - 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 Result.getOrThrowUnauthorized(): T = getOrElse { - throw if (it is HttpException && it.code() in listOf(401, 403)) { - NetworkError.Unauthorized(it) - } else { - it - } -}