Fixed a bug with favourites

This commit is contained in:
2025-08-01 13:57:52 -06:00
parent 49c9a6dce1
commit 571db144c4
20 changed files with 566 additions and 429 deletions

View File

@@ -12,6 +12,7 @@ import com.atridad.mealient.datasource.models.GetShoppingListItemResponse
import com.atridad.mealient.datasource.models.GetShoppingListResponse
import com.atridad.mealient.datasource.models.GetShoppingListsResponse
import com.atridad.mealient.datasource.models.GetUnitsResponse
import com.atridad.mealient.datasource.models.GetUserFavoritesResponse
import com.atridad.mealient.datasource.models.GetUserInfoResponse
import com.atridad.mealient.datasource.models.ParseRecipeURLRequest
import com.atridad.mealient.datasource.models.UpdateRecipeRequest
@@ -20,39 +21,37 @@ import com.atridad.mealient.datasource.models.VersionResponse
interface MealieDataSource {
suspend fun createRecipe(
recipe: CreateRecipeRequest,
recipe: CreateRecipeRequest,
): String
suspend fun updateRecipe(
slug: String,
recipe: UpdateRecipeRequest,
slug: String,
recipe: UpdateRecipeRequest,
): GetRecipeResponse
/**
* Tries to acquire authentication token using the provided credentials
*/
/** Tries to acquire authentication token using the provided credentials */
suspend fun authenticate(
username: String,
password: String,
username: String,
password: String,
): String
suspend fun getVersionInfo(baseURL: String): VersionResponse
suspend fun requestRecipes(
page: Int,
perPage: Int,
page: Int,
perPage: Int,
): List<GetRecipeSummaryResponse>
suspend fun requestRecipeInfo(
slug: String,
slug: String,
): GetRecipeResponse
suspend fun parseRecipeFromURL(
request: ParseRecipeURLRequest,
request: ParseRecipeURLRequest,
): String
suspend fun createApiToken(
request: CreateApiTokenRequest,
request: CreateApiTokenRequest,
): CreateApiTokenResponse
suspend fun requestUserInfo(): GetUserInfoResponse
@@ -82,4 +81,6 @@ interface MealieDataSource {
suspend fun deleteShoppingList(id: String)
suspend fun updateShoppingListName(id: String, name: String)
}
suspend fun getUserFavoritesAlternative(userId: String): GetUserFavoritesResponse
}

View File

@@ -12,6 +12,7 @@ import com.atridad.mealient.datasource.models.GetShoppingListResponse
import com.atridad.mealient.datasource.models.GetShoppingListsResponse
import com.atridad.mealient.datasource.models.GetTokenResponse
import com.atridad.mealient.datasource.models.GetUnitsResponse
import com.atridad.mealient.datasource.models.GetUserFavoritesResponse
import com.atridad.mealient.datasource.models.GetUserInfoResponse
import com.atridad.mealient.datasource.models.ParseRecipeURLRequest
import com.atridad.mealient.datasource.models.UpdateRecipeRequest
@@ -25,8 +26,8 @@ internal interface MealieService {
suspend fun createRecipe(addRecipeRequest: CreateRecipeRequest): String
suspend fun updateRecipe(
addRecipeRequest: UpdateRecipeRequest,
slug: String,
addRecipeRequest: UpdateRecipeRequest,
slug: String,
): GetRecipeResponse
suspend fun getVersion(baseURL: String): VersionResponse
@@ -68,6 +69,8 @@ internal interface MealieService {
suspend fun deleteShoppingList(id: String)
suspend fun updateShoppingList(id: String, request: JsonElement)
suspend fun getShoppingListJson(id: String) : JsonElement
}
suspend fun getShoppingListJson(id: String): JsonElement
suspend fun getUserFavoritesAlternative(userId: String): GetUserFavoritesResponse
}

View File

@@ -17,6 +17,7 @@ import com.atridad.mealient.datasource.models.GetShoppingListItemResponse
import com.atridad.mealient.datasource.models.GetShoppingListResponse
import com.atridad.mealient.datasource.models.GetShoppingListsResponse
import com.atridad.mealient.datasource.models.GetUnitsResponse
import com.atridad.mealient.datasource.models.GetUserFavoritesResponse
import com.atridad.mealient.datasource.models.GetUserInfoResponse
import com.atridad.mealient.datasource.models.ParseRecipeURLRequest
import com.atridad.mealient.datasource.models.UpdateRecipeRequest
@@ -24,251 +25,309 @@ import com.atridad.mealient.datasource.models.VersionResponse
import io.ktor.client.call.NoTransformationFoundException
import io.ktor.client.call.body
import io.ktor.client.plugins.ResponseException
import java.net.SocketException
import java.net.SocketTimeoutException
import javax.inject.Inject
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject
import java.net.SocketException
import java.net.SocketTimeoutException
import javax.inject.Inject
internal class MealieDataSourceImpl @Inject constructor(
private val networkRequestWrapper: NetworkRequestWrapper,
private val service: MealieService,
internal class MealieDataSourceImpl
@Inject
constructor(
private val networkRequestWrapper: NetworkRequestWrapper,
private val service: MealieService,
) : MealieDataSource {
override suspend fun createRecipe(
recipe: CreateRecipeRequest,
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createRecipe(recipe) },
logMethod = { "createRecipe" },
logParameters = { "recipe = $recipe" }
).trim('"')
recipe: CreateRecipeRequest,
): String =
networkRequestWrapper
.makeCallAndHandleUnauthorized(
block = { service.createRecipe(recipe) },
logMethod = { "createRecipe" },
logParameters = { "recipe = $recipe" }
)
.trim('"')
override suspend fun updateRecipe(
slug: String,
recipe: UpdateRecipeRequest,
): GetRecipeResponse = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.updateRecipe(recipe, slug) },
logMethod = { "updateRecipe" },
logParameters = { "slug = $slug, recipe = $recipe" }
)
slug: String,
recipe: UpdateRecipeRequest,
): GetRecipeResponse =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.updateRecipe(recipe, slug) },
logMethod = { "updateRecipe" },
logParameters = { "slug = $slug, recipe = $recipe" }
)
override suspend fun authenticate(
username: String,
password: String,
): String = networkRequestWrapper.makeCall(
block = { service.getToken(username, password) },
logMethod = { "authenticate" },
logParameters = { "username = $username, password = $password" }
).map { it.accessToken }.getOrElse {
val errorDetail = (it as? ResponseException)?.response?.body<ErrorDetail>() ?: throw it
throw if (errorDetail.detail == "Unauthorized") NetworkError.Unauthorized(it) else it
}
username: String,
password: String,
): String =
networkRequestWrapper
.makeCall(
block = { service.getToken(username, password) },
logMethod = { "authenticate" },
logParameters = { "username = $username, password = $password" }
)
.map { it.accessToken }
.getOrElse {
val errorDetail =
(it as? ResponseException)?.response?.body<ErrorDetail>()
?: throw it
throw if (errorDetail.detail == "Unauthorized")
NetworkError.Unauthorized(it)
else it
}
override suspend fun getVersionInfo(baseURL: String): VersionResponse =
networkRequestWrapper.makeCall(
block = { service.getVersion(baseURL) },
logMethod = { "getVersionInfo" },
logParameters = { "baseURL = $baseURL" }
).getOrElse {
throw when (it) {
is ResponseException, is NoTransformationFoundException -> NetworkError.NotMealie(it)
is SocketTimeoutException, is SocketException -> NetworkError.NoServerConnection(it)
else -> NetworkError.MalformedUrl(it)
}
}
networkRequestWrapper.makeCall(
block = { service.getVersion(baseURL) },
logMethod = { "getVersionInfo" },
logParameters = { "baseURL = $baseURL" }
)
.getOrElse {
throw when (it) {
is ResponseException, is NoTransformationFoundException ->
NetworkError.NotMealie(it)
is SocketTimeoutException, is SocketException ->
NetworkError.NoServerConnection(it)
else -> NetworkError.MalformedUrl(it)
}
}
override suspend fun requestRecipes(
page: Int,
perPage: Int,
): List<GetRecipeSummaryResponse> = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getRecipeSummary(page, perPage) },
logMethod = { "requestRecipes" },
logParameters = { "page = $page, perPage = $perPage" }
).items
page: Int,
perPage: Int,
): List<GetRecipeSummaryResponse> {
val response =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getRecipeSummary(page, perPage) },
logMethod = { "requestRecipes" },
logParameters = { "page = $page, perPage = $perPage" }
)
return response.items
}
override suspend fun requestRecipeInfo(
slug: String,
): GetRecipeResponse = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getRecipe(slug) },
logMethod = { "requestRecipeInfo" },
logParameters = { "slug = $slug" }
)
slug: String,
): GetRecipeResponse =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getRecipe(slug) },
logMethod = { "requestRecipeInfo" },
logParameters = { "slug = $slug" }
)
override suspend fun parseRecipeFromURL(
request: ParseRecipeURLRequest,
): String = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createRecipeFromURL(request) },
logMethod = { "parseRecipeFromURL" },
logParameters = { "request = $request" }
)
request: ParseRecipeURLRequest,
): String =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createRecipeFromURL(request) },
logMethod = { "parseRecipeFromURL" },
logParameters = { "request = $request" }
)
override suspend fun createApiToken(
request: CreateApiTokenRequest,
): CreateApiTokenResponse = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createApiToken(request) },
logMethod = { "createApiToken" },
logParameters = { "request = $request" }
)
request: CreateApiTokenRequest,
): CreateApiTokenResponse =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createApiToken(request) },
logMethod = { "createApiToken" },
logParameters = { "request = $request" }
)
override suspend fun requestUserInfo(): GetUserInfoResponse {
return networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getUserSelfInfo() },
logMethod = { "requestUserInfo" },
)
val response =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getUserSelfInfo() },
logMethod = { "requestUserInfo" },
)
return response
}
override suspend fun removeFavoriteRecipe(
userId: String,
recipeSlug: String,
): Unit = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.removeFavoriteRecipe(userId, recipeSlug) },
logMethod = { "removeFavoriteRecipe" },
logParameters = { "userId = $userId, recipeSlug = $recipeSlug" }
)
userId: String,
recipeSlug: String,
): Unit {
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.removeFavoriteRecipe(userId, recipeSlug) },
logMethod = { "removeFavoriteRecipe" },
logParameters = { "userId = $userId, recipeSlug = $recipeSlug" }
)
}
override suspend fun addFavoriteRecipe(
userId: String,
recipeSlug: String,
): Unit = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.addFavoriteRecipe(userId, recipeSlug) },
logMethod = { "addFavoriteRecipe" },
logParameters = { "userId = $userId, recipeSlug = $recipeSlug" }
)
userId: String,
recipeSlug: String,
): Unit {
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.addFavoriteRecipe(userId, recipeSlug) },
logMethod = { "addFavoriteRecipe" },
logParameters = { "userId = $userId, recipeSlug = $recipeSlug" }
)
}
override suspend fun deleteRecipe(
slug: String,
): Unit = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.deleteRecipe(slug) },
logMethod = { "deleteRecipe" },
logParameters = { "slug = $slug" }
)
slug: String,
): Unit =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.deleteRecipe(slug) },
logMethod = { "deleteRecipe" },
logParameters = { "slug = $slug" }
)
override suspend fun getShoppingLists(
page: Int,
perPage: Int,
): GetShoppingListsResponse = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getShoppingLists(page, perPage) },
logMethod = { "getShoppingLists" },
logParameters = { "page = $page, perPage = $perPage" }
)
page: Int,
perPage: Int,
): GetShoppingListsResponse =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getShoppingLists(page, perPage) },
logMethod = { "getShoppingLists" },
logParameters = { "page = $page, perPage = $perPage" }
)
override suspend fun getShoppingList(
id: String,
): GetShoppingListResponse = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getShoppingList(id) },
logMethod = { "getShoppingList" },
logParameters = { "id = $id" }
)
id: String,
): GetShoppingListResponse =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getShoppingList(id) },
logMethod = { "getShoppingList" },
logParameters = { "id = $id" }
)
private suspend fun getShoppingListItem(
id: String,
): JsonElement = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getShoppingListItem(id) },
logMethod = { "getShoppingListItem" },
logParameters = { "id = $id" }
)
id: String,
): JsonElement =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getShoppingListItem(id) },
logMethod = { "getShoppingListItem" },
logParameters = { "id = $id" }
)
private suspend fun updateShoppingListItem(
id: String,
request: JsonElement,
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.updateShoppingListItem(id, request) },
logMethod = { "updateShoppingListItem" },
logParameters = { "id = $id, request = $request" }
)
id: String,
request: JsonElement,
) =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.updateShoppingListItem(id, request) },
logMethod = { "updateShoppingListItem" },
logParameters = { "id = $id, request = $request" }
)
override suspend fun deleteShoppingListItem(
id: String,
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.deleteShoppingListItem(id) },
logMethod = { "deleteShoppingListItem" },
logParameters = { "id = $id" }
)
id: String,
) =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.deleteShoppingListItem(id) },
logMethod = { "deleteShoppingListItem" },
logParameters = { "id = $id" }
)
override suspend fun updateShoppingListItem(
item: GetShoppingListItemResponse,
item: GetShoppingListItemResponse,
) {
// Has to be done in two steps because we can't specify only the changed fields
val remoteItem = getShoppingListItem(item.id)
val updatedItem = remoteItem.jsonObject.toMutableMap().apply {
put("checked", JsonPrimitive(item.checked))
put("isFood", JsonPrimitive(item.isFood))
put("note", JsonPrimitive(item.note))
put("quantity", JsonPrimitive(item.quantity))
put("foodId", JsonPrimitive(item.food?.id))
put("unitId", JsonPrimitive(item.unit?.id))
remove("unit")
remove("food")
}
val updatedItem =
remoteItem.jsonObject.toMutableMap().apply {
put("checked", JsonPrimitive(item.checked))
put("isFood", JsonPrimitive(item.isFood))
put("note", JsonPrimitive(item.note))
put("quantity", JsonPrimitive(item.quantity))
put("foodId", JsonPrimitive(item.food?.id))
put("unitId", JsonPrimitive(item.unit?.id))
remove("unit")
remove("food")
}
updateShoppingListItem(item.id, JsonObject(updatedItem))
}
override suspend fun getFoods(): GetFoodsResponse {
return networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getFoods(perPage = -1) },
logMethod = { "getFoods" },
block = { service.getFoods(perPage = -1) },
logMethod = { "getFoods" },
)
}
override suspend fun getUnits(): GetUnitsResponse {
return networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getUnits(perPage = -1) },
logMethod = { "getUnits" },
block = { service.getUnits(perPage = -1) },
logMethod = { "getUnits" },
)
}
override suspend fun addShoppingListItem(
request: CreateShoppingListItemRequest,
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createShoppingListItem(request) },
logMethod = { "addShoppingListItem" },
logParameters = { "request = $request" }
)
request: CreateShoppingListItemRequest,
) =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createShoppingListItem(request) },
logMethod = { "addShoppingListItem" },
logParameters = { "request = $request" }
)
override suspend fun addShoppingList(
request: CreateShoppingListRequest,
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createShoppingList(request) },
logMethod = { "createShoppingList" },
logParameters = { "request = $request" }
)
request: CreateShoppingListRequest,
) =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.createShoppingList(request) },
logMethod = { "createShoppingList" },
logParameters = { "request = $request" }
)
private suspend fun updateShoppingList(
id: String,
request: JsonElement,
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.updateShoppingList(id, request) },
logMethod = { "updateShoppingList" },
logParameters = { "id = $id, request = $request" }
)
id: String,
request: JsonElement,
) =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.updateShoppingList(id, request) },
logMethod = { "updateShoppingList" },
logParameters = { "id = $id, request = $request" }
)
private suspend fun getShoppingListJson(
id: String,
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getShoppingListJson(id) },
logMethod = { "getShoppingListJson" },
logParameters = { "id = $id" }
)
id: String,
) =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getShoppingListJson(id) },
logMethod = { "getShoppingListJson" },
logParameters = { "id = $id" }
)
override suspend fun deleteShoppingList(
id: String,
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.deleteShoppingList(id) },
logMethod = { "deleteShoppingList" },
logParameters = { "id = $id" }
)
id: String,
) =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.deleteShoppingList(id) },
logMethod = { "deleteShoppingList" },
logParameters = { "id = $id" }
)
override suspend fun updateShoppingListName(
id: String,
name: String
) {
override suspend fun updateShoppingListName(id: String, name: String) {
// Has to be done in two steps because we can't specify only the changed fields
val remoteItem = getShoppingListJson(id)
val updatedItem = remoteItem.jsonObject.toMutableMap().apply {
put("name", JsonPrimitive(name))
}.let(::JsonObject)
val updatedItem =
remoteItem
.jsonObject
.toMutableMap()
.apply { put("name", JsonPrimitive(name)) }
.let(::JsonObject)
updateShoppingList(id, updatedItem)
}
override suspend fun getUserFavoritesAlternative(userId: String): GetUserFavoritesResponse {
val response =
networkRequestWrapper.makeCallAndHandleUnauthorized(
block = { service.getUserFavoritesAlternative(userId) },
logMethod = { "getUserFavoritesAlternative" },
logParameters = { "userId = $userId" }
)
return response
}
}

View File

@@ -14,6 +14,7 @@ import com.atridad.mealient.datasource.models.GetShoppingListResponse
import com.atridad.mealient.datasource.models.GetShoppingListsResponse
import com.atridad.mealient.datasource.models.GetTokenResponse
import com.atridad.mealient.datasource.models.GetUnitsResponse
import com.atridad.mealient.datasource.models.GetUserFavoritesResponse
import com.atridad.mealient.datasource.models.GetUserInfoResponse
import com.atridad.mealient.datasource.models.ParseRecipeURLRequest
import com.atridad.mealient.datasource.models.UpdateRecipeRequest
@@ -34,13 +35,15 @@ import io.ktor.http.contentType
import io.ktor.http.parameters
import io.ktor.http.path
import io.ktor.http.takeFrom
import kotlinx.serialization.json.JsonElement
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.serialization.json.JsonElement
internal class MealieServiceKtor @Inject constructor(
private val httpClient: HttpClient,
private val serverUrlProviderProvider: Provider<ServerUrlProvider>,
internal class MealieServiceKtor
@Inject
constructor(
private val httpClient: HttpClient,
private val serverUrlProviderProvider: Provider<ServerUrlProvider>,
) : MealieService {
private val serverUrlProvider: ServerUrlProvider
@@ -52,111 +55,109 @@ internal class MealieServiceKtor @Inject constructor(
append("password", password)
}
return httpClient.post {
endpoint("/api/auth/token")
setBody(FormDataContent(formParameters))
}.body()
return httpClient
.post {
endpoint("/api/auth/token")
setBody(FormDataContent(formParameters))
}
.body()
}
override suspend fun createRecipe(addRecipeRequest: CreateRecipeRequest): String {
return httpClient.post {
endpoint("/api/recipes")
contentType(ContentType.Application.Json)
setBody(addRecipeRequest)
}.body()
return httpClient
.post {
endpoint("/api/recipes")
contentType(ContentType.Application.Json)
setBody(addRecipeRequest)
}
.body()
}
override suspend fun updateRecipe(
addRecipeRequest: UpdateRecipeRequest,
slug: String,
addRecipeRequest: UpdateRecipeRequest,
slug: String,
): GetRecipeResponse {
return httpClient.patch {
endpoint("/api/recipes/$slug")
contentType(ContentType.Application.Json)
setBody(addRecipeRequest)
}.body()
return httpClient
.patch {
endpoint("/api/recipes/$slug")
contentType(ContentType.Application.Json)
setBody(addRecipeRequest)
}
.body()
}
override suspend fun getVersion(baseURL: String): VersionResponse {
return httpClient.get {
endpoint(baseURL, "/api/app/about")
}.body()
return httpClient.get { endpoint(baseURL, "/api/app/about") }.body()
}
override suspend fun getRecipeSummary(page: Int, perPage: Int): GetRecipesResponse {
return httpClient.get {
endpoint("/api/recipes") {
parameters.append("page", page.toString())
parameters.append("perPage", perPage.toString())
}
}.body()
return httpClient
.get {
endpoint("/api/recipes") {
parameters.append("page", page.toString())
parameters.append("perPage", perPage.toString())
}
}
.body()
}
override suspend fun getRecipe(slug: String): GetRecipeResponse {
return httpClient.get {
endpoint("/api/recipes/$slug")
}.body()
return httpClient.get { endpoint("/api/recipes/$slug") }.body()
}
override suspend fun createRecipeFromURL(request: ParseRecipeURLRequest): String {
return httpClient.post {
endpoint("/api/recipes/create-url")
contentType(ContentType.Application.Json)
setBody(request)
}.body()
return httpClient
.post {
endpoint("/api/recipes/create-url")
contentType(ContentType.Application.Json)
setBody(request)
}
.body()
}
override suspend fun createApiToken(request: CreateApiTokenRequest): CreateApiTokenResponse {
return httpClient.post {
endpoint("/api/users/api-tokens")
contentType(ContentType.Application.Json)
setBody(request)
}.body()
return httpClient
.post {
endpoint("/api/users/api-tokens")
contentType(ContentType.Application.Json)
setBody(request)
}
.body()
}
override suspend fun getUserSelfInfo(): GetUserInfoResponse {
return httpClient.get {
endpoint("/api/users/self")
}.body()
return httpClient.get { endpoint("/api/users/self") }.body()
}
override suspend fun removeFavoriteRecipe(userId: String, recipeSlug: String) {
httpClient.delete {
endpoint("/api/users/$userId/favorites/$recipeSlug")
}
httpClient.delete { endpoint("/api/users/$userId/favorites/$recipeSlug") }
}
override suspend fun addFavoriteRecipe(userId: String, recipeSlug: String) {
httpClient.post {
endpoint("/api/users/$userId/favorites/$recipeSlug")
}
httpClient.post { endpoint("/api/users/$userId/favorites/$recipeSlug") }
}
override suspend fun deleteRecipe(slug: String) {
httpClient.delete {
endpoint("/api/recipes/$slug")
}
httpClient.delete { endpoint("/api/recipes/$slug") }
}
override suspend fun getShoppingLists(page: Int, perPage: Int): GetShoppingListsResponse {
return httpClient.get {
endpoint("/api/households/shopping/lists") {
parameters.append("page", page.toString())
parameters.append("perPage", perPage.toString())
}
}.body()
return httpClient
.get {
endpoint("/api/households/shopping/lists") {
parameters.append("page", page.toString())
parameters.append("perPage", perPage.toString())
}
}
.body()
}
override suspend fun getShoppingList(id: String): GetShoppingListResponse {
return httpClient.get {
endpoint("/api/households/shopping/lists/$id")
}.body()
return httpClient.get { endpoint("/api/households/shopping/lists/$id") }.body()
}
override suspend fun getShoppingListItem(id: String): JsonElement {
return httpClient.get {
endpoint("/api/households/shopping/items/$id")
}.body()
return httpClient.get { endpoint("/api/households/shopping/items/$id") }.body()
}
override suspend fun updateShoppingListItem(id: String, request: JsonElement) {
@@ -168,25 +169,19 @@ internal class MealieServiceKtor @Inject constructor(
}
override suspend fun deleteShoppingListItem(id: String) {
httpClient.delete {
endpoint("/api/households/shopping/items/$id")
}
httpClient.delete { endpoint("/api/households/shopping/items/$id") }
}
override suspend fun getFoods(perPage: Int): GetFoodsResponse {
return httpClient.get {
endpoint("/api/foods") {
parameters.append("perPage", perPage.toString())
}
}.body()
return httpClient
.get { endpoint("/api/foods") { parameters.append("perPage", perPage.toString()) } }
.body()
}
override suspend fun getUnits(perPage: Int): GetUnitsResponse {
return httpClient.get {
endpoint("/api/units") {
parameters.append("perPage", perPage.toString())
}
}.body()
return httpClient
.get { endpoint("/api/units") { parameters.append("perPage", perPage.toString()) } }
.body()
}
override suspend fun createShoppingListItem(request: CreateShoppingListItemRequest) {
@@ -206,9 +201,7 @@ internal class MealieServiceKtor @Inject constructor(
}
override suspend fun deleteShoppingList(id: String) {
httpClient.delete {
endpoint("/api/households/shopping/lists/$id")
}
httpClient.delete { endpoint("/api/households/shopping/lists/$id") }
}
override suspend fun updateShoppingList(id: String, request: JsonElement) {
@@ -220,27 +213,25 @@ internal class MealieServiceKtor @Inject constructor(
}
override suspend fun getShoppingListJson(id: String): JsonElement {
return httpClient.get {
endpoint("/api/households/shopping/lists/$id")
}.body()
return httpClient.get { endpoint("/api/households/shopping/lists/$id") }.body()
}
override suspend fun getUserFavoritesAlternative(userId: String): GetUserFavoritesResponse {
return httpClient.get { endpoint("/api/users/$userId/favorites") }.body()
}
private suspend fun HttpRequestBuilder.endpoint(
path: String,
block: URLBuilder.() -> Unit = {},
path: String,
block: URLBuilder.() -> Unit = {},
) {
val baseUrl = checkNotNull(serverUrlProvider.getUrl()) { "Server URL is not set" }
endpoint(
baseUrl = baseUrl,
path = path,
block = block
)
endpoint(baseUrl = baseUrl, path = path, block = block)
}
private fun HttpRequestBuilder.endpoint(
baseUrl: String,
path: String,
block: URLBuilder.() -> Unit = {},
baseUrl: String,
path: String,
block: URLBuilder.() -> Unit = {},
) {
url {
takeFrom(baseUrl)

View File

@@ -0,0 +1,18 @@
package com.atridad.mealient.datasource.models
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class GetUserFavoritesResponse(
@SerialName("ratings") val ratings: List<UserRatingResponse> = emptyList(),
)
@Serializable
data class UserRatingResponse(
@SerialName("recipeId") val recipeId: String,
@SerialName("rating") val rating: Double? = null,
@SerialName("isFavorite") val isFavorite: Boolean,
@SerialName("userId") val userId: String,
@SerialName("id") val id: String,
)