From 95205f8ffeed8eec09b3b7ee2d1ad1550aed9ee2 Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Sun, 7 Aug 2022 17:19:35 +0200 Subject: [PATCH] Initialize v1 support --- .../mealient/data/recipes/RecipeRepo.kt | 2 +- .../mealient/data/recipes/db/RecipeStorage.kt | 2 +- .../data/recipes/db/RecipeStorageImpl.kt | 2 +- .../data/recipes/impl/RecipeRepoImpl.kt | 2 +- .../extensions/RemoteToLocalMappings.kt | 4 +- .../ui/recipes/images/RecipeModelLoader.kt | 2 +- .../ui/recipes/info/RecipeInfoViewModel.kt | 2 +- app/src/main/res/navigation/nav_graph.xml | 2 +- .../3.json | 404 ++++++++++++++++++ .../gq/kirmanak/mealient/database/AppDb.kt | 4 +- .../mealient/database/DatabaseModule.kt | 4 +- .../mealient/database/recipe/RecipeDao.kt | 6 +- .../recipe/entity/CategoryRecipeEntity.kt | 2 +- .../database/recipe/entity/RecipeEntity.kt | 2 +- .../recipe/entity/RecipeIngredientEntity.kt | 4 +- .../recipe/entity/RecipeInstructionEntity.kt | 2 +- .../recipe/entity/RecipeSummaryEntity.kt | 2 +- .../database/recipe/entity/TagRecipeEntity.kt | 2 +- .../datasource/MealieDataSourceImpl.kt | 31 +- .../mealient/datasource/MealieService.kt | 8 + .../models/GetRecipeIngredientResponse.kt | 2 +- .../datasource/models/GetRecipeResponse.kt | 2 +- .../models/GetRecipeSummaryResponse.kt | 2 +- .../datasource/models/GetRecipesResponseV1.kt | 9 + 24 files changed, 478 insertions(+), 26 deletions(-) create mode 100644 database/schemas/gq.kirmanak.mealient.database.AppDb/3.json create mode 100644 datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipesResponseV1.kt diff --git a/app/src/main/java/gq/kirmanak/mealient/data/recipes/RecipeRepo.kt b/app/src/main/java/gq/kirmanak/mealient/data/recipes/RecipeRepo.kt index 503a7b2..9c42bf0 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/recipes/RecipeRepo.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/recipes/RecipeRepo.kt @@ -9,5 +9,5 @@ interface RecipeRepo { suspend fun clearLocalData() - suspend fun loadRecipeInfo(recipeId: Long, recipeSlug: String): FullRecipeInfo + suspend fun loadRecipeInfo(recipeId: String, recipeSlug: String): FullRecipeInfo } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/recipes/db/RecipeStorage.kt b/app/src/main/java/gq/kirmanak/mealient/data/recipes/db/RecipeStorage.kt index 0ff12a5..a36e84d 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/recipes/db/RecipeStorage.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/recipes/db/RecipeStorage.kt @@ -17,5 +17,5 @@ interface RecipeStorage { suspend fun saveRecipeInfo(recipe: GetRecipeResponse) - suspend fun queryRecipeInfo(recipeId: Long): FullRecipeInfo + suspend fun queryRecipeInfo(recipeId: String): FullRecipeInfo } \ No newline at end of file diff --git a/app/src/main/java/gq/kirmanak/mealient/data/recipes/db/RecipeStorageImpl.kt b/app/src/main/java/gq/kirmanak/mealient/data/recipes/db/RecipeStorageImpl.kt index e5d777f..702f5e3 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/recipes/db/RecipeStorageImpl.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/recipes/db/RecipeStorageImpl.kt @@ -132,7 +132,7 @@ class RecipeStorageImpl @Inject constructor( } } - override suspend fun queryRecipeInfo(recipeId: Long): FullRecipeInfo { + override suspend fun queryRecipeInfo(recipeId: String): FullRecipeInfo { logger.v { "queryRecipeInfo() called with: recipeId = $recipeId" } val fullRecipeInfo = checkNotNull(recipeDao.queryFullRecipeInfo(recipeId)) { "Can't find recipe by id $recipeId in DB" diff --git a/app/src/main/java/gq/kirmanak/mealient/data/recipes/impl/RecipeRepoImpl.kt b/app/src/main/java/gq/kirmanak/mealient/data/recipes/impl/RecipeRepoImpl.kt index 942fe5f..5398aae 100644 --- a/app/src/main/java/gq/kirmanak/mealient/data/recipes/impl/RecipeRepoImpl.kt +++ b/app/src/main/java/gq/kirmanak/mealient/data/recipes/impl/RecipeRepoImpl.kt @@ -38,7 +38,7 @@ class RecipeRepoImpl @Inject constructor( storage.clearAllLocalData() } - override suspend fun loadRecipeInfo(recipeId: Long, recipeSlug: String): FullRecipeInfo { + override suspend fun loadRecipeInfo(recipeId: String, recipeSlug: String): FullRecipeInfo { logger.v { "loadRecipeInfo() called with: recipeId = $recipeId, recipeSlug = $recipeSlug" } runCatchingExceptCancel { diff --git a/app/src/main/java/gq/kirmanak/mealient/extensions/RemoteToLocalMappings.kt b/app/src/main/java/gq/kirmanak/mealient/extensions/RemoteToLocalMappings.kt index 2cd87a3..bf13089 100644 --- a/app/src/main/java/gq/kirmanak/mealient/extensions/RemoteToLocalMappings.kt +++ b/app/src/main/java/gq/kirmanak/mealient/extensions/RemoteToLocalMappings.kt @@ -13,7 +13,7 @@ fun GetRecipeResponse.toRecipeEntity() = RecipeEntity( recipeYield = recipeYield ) -fun GetRecipeIngredientResponse.toRecipeIngredientEntity(remoteId: Long) = +fun GetRecipeIngredientResponse.toRecipeIngredientEntity(remoteId: String) = RecipeIngredientEntity( recipeId = remoteId, title = title, @@ -24,7 +24,7 @@ fun GetRecipeIngredientResponse.toRecipeIngredientEntity(remoteId: Long) = quantity = quantity ) -fun GetRecipeInstructionResponse.toRecipeInstructionEntity(remoteId: Long) = +fun GetRecipeInstructionResponse.toRecipeInstructionEntity(remoteId: String) = RecipeInstructionEntity( recipeId = remoteId, title = title, diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/images/RecipeModelLoader.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/images/RecipeModelLoader.kt index 45a7e4c..edb9704 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/images/RecipeModelLoader.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/images/RecipeModelLoader.kt @@ -44,7 +44,7 @@ class RecipeModelLoader private constructor( options: Options? ): String? { logger.v { "getUrl() called with: model = $model, width = $width, height = $height, options = $options" } - return runBlocking { recipeImageUrlProvider.generateImageUrl(model?.slug) } + return runBlocking { recipeImageUrlProvider.generateImageUrl(model?.remoteId) } } override fun getHeaders( diff --git a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/info/RecipeInfoViewModel.kt b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/info/RecipeInfoViewModel.kt index cf40aba..5f1591b 100644 --- a/app/src/main/java/gq/kirmanak/mealient/ui/recipes/info/RecipeInfoViewModel.kt +++ b/app/src/main/java/gq/kirmanak/mealient/ui/recipes/info/RecipeInfoViewModel.kt @@ -20,7 +20,7 @@ class RecipeInfoViewModel @Inject constructor( private val _uiState = MutableLiveData(RecipeInfoUiState()) val uiState: LiveData get() = _uiState - fun loadRecipeInfo(recipeId: Long, recipeSlug: String) { + fun loadRecipeInfo(recipeId: String, recipeSlug: String) { logger.v { "loadRecipeInfo() called with: recipeId = $recipeId, recipeSlug = $recipeSlug" } _uiState.value = RecipeInfoUiState() viewModelScope.launch { diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 47fa15b..01445d5 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -36,7 +36,7 @@ app:argType="string" /> + app:argType="string" /> getVersionInfoV1(baseUrl) + is SocketTimeoutException, + is ConnectException -> throw NetworkError.NoServerConnection(it) + else -> throw NetworkError.MalformedUrl(it) + } + } + + private suspend fun getVersionInfoV1(baseUrl: String): VersionResponse = makeCall( + block = { getVersion("$baseUrl/api/app/about") }, + logMethod = { "getVersionInfoV1" }, + logParameters = { "baseUrl = $baseUrl" }, ).getOrElse { throw when (it) { is HttpException, is SerializationException -> NetworkError.NotMealie(it) @@ -58,7 +71,23 @@ class MealieDataSourceImpl @Inject constructor( block = { getRecipeSummary("$baseUrl/api/recipes/summary", token, start, limit) }, logMethod = { "requestRecipes" }, logParameters = { "baseUrl = $baseUrl, token = $token, start = $start, limit = $limit" } - ).getOrThrowUnauthorized() + ).getOrElse { + val code = (it as? HttpException)?.code() ?: throw it + if (code == 404) requestRecipesV1(baseUrl, token, start, limit) else throw it + } + + private suspend fun requestRecipesV1( + baseUrl: String, token: String?, start: Int, limit: Int + ): List { + // Imagine start is 30 and limit is 15. It means that we already have page 1 and 2, now we need page 3 + val perPage = limit + val page = start / perPage + 1 + return makeCall( + block = { getRecipeSummaryV1("$baseUrl/api/recipes", token, page, perPage) }, + logMethod = { "requestRecipesV1" }, + logParameters = { "baseUrl = $baseUrl, token = $token, start = $start, limit = $limit" } + ).map { it.items }.getOrThrowUnauthorized() + } override suspend fun requestRecipeInfo( baseUrl: String, token: String?, slug: String diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/MealieService.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/MealieService.kt index 9750cf9..6067062 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/MealieService.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/MealieService.kt @@ -34,6 +34,14 @@ interface MealieService { @Query("limit") limit: Int, ): List + @GET + suspend fun getRecipeSummaryV1( + @Url url: String, + @Header(AUTHORIZATION_HEADER_NAME) token: String?, + @Query("page") page: Int, + @Query("perPage") perPage: Int, + ): GetRecipesResponseV1 + @GET suspend fun getRecipe( @Url url: String, diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeIngredientResponse.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeIngredientResponse.kt index e00a9fb..257d038 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeIngredientResponse.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeIngredientResponse.kt @@ -10,5 +10,5 @@ data class GetRecipeIngredientResponse( @SerialName("unit") val unit: String = "", @SerialName("food") val food: String = "", @SerialName("disableAmount") val disableAmount: Boolean, - @SerialName("quantity") val quantity: Int, + @SerialName("quantity") val quantity: Double, ) diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeResponse.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeResponse.kt index 3197d75..36f9ee4 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeResponse.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeResponse.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable @Serializable data class GetRecipeResponse( - @SerialName("id") val remoteId: Long, + @SerialName("id") val remoteId: String, @SerialName("name") val name: String, @SerialName("slug") val slug: String, @SerialName("image") val image: String, diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeSummaryResponse.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeSummaryResponse.kt index f9fc613..828811e 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeSummaryResponse.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipeSummaryResponse.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable @Serializable data class GetRecipeSummaryResponse( - @SerialName("id") val remoteId: Long, + @SerialName("id") val remoteId: String, @SerialName("name") val name: String, @SerialName("slug") val slug: String, @SerialName("image") val image: String?, diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipesResponseV1.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipesResponseV1.kt new file mode 100644 index 0000000..5977b74 --- /dev/null +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/models/GetRecipesResponseV1.kt @@ -0,0 +1,9 @@ +package gq.kirmanak.mealient.datasource.models + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +data class GetRecipesResponseV1( + @SerialName("items") val items: List, +) \ No newline at end of file