From 29e3efdf5e30fa5f892b2cf21b87434aa1c5230c Mon Sep 17 00:00:00 2001 From: Leropsis Date: Sun, 20 Jul 2025 22:56:09 +0300 Subject: [PATCH 1/2] chore: add search for shopping list --- .../scala/api/ingredients/Endpoints.scala | 1 + .../ingredients/SearchForShoppingList.scala | 56 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 src/main/scala/api/ingredients/SearchForShoppingList.scala diff --git a/src/main/scala/api/ingredients/Endpoints.scala b/src/main/scala/api/ingredients/Endpoints.scala index 3adcbcde..6fb56090 100644 --- a/src/main/scala/api/ingredients/Endpoints.scala +++ b/src/main/scala/api/ingredients/Endpoints.scala @@ -21,5 +21,6 @@ val ingredientsEndpoints = List( search.widen, searchForRecipe.widen, searchForStorage.widen, + searchForShoppingList.widen, requestPublication.widen ) ++ publicEndpoints.map(_.widen) diff --git a/src/main/scala/api/ingredients/SearchForShoppingList.scala b/src/main/scala/api/ingredients/SearchForShoppingList.scala new file mode 100644 index 00000000..947a47b0 --- /dev/null +++ b/src/main/scala/api/ingredients/SearchForShoppingList.scala @@ -0,0 +1,56 @@ +package api.ingredients + +import api.Authentication.{AuthenticatedUser, zSecuredServerLogic} +import api.common.search.* +import api.EndpointErrorVariants.serverErrorVariant +import db.QuillConfig.provideDS +import db.repositories.IngredientsQueries +import db.tables.DbShoppingList +import domain.{IngredientId, InternalServerError} + +import io.circe.generic.auto.* +import io.getquill.{query => quillQuery, *} +import javax.sql.DataSource +import sttp.tapir.generic.auto.* +import sttp.tapir.json.circe.* +import sttp.tapir.ztapir.* +import zio.ZIO + +final case class IngredientsForShoppingListResp( + id: IngredientId, + name: String, + isInShopList: Boolean +) extends Searchable + +private type SearchForShopListEnv = DataSource + +private val searchForShoppingList: ZServerEndpoint[SearchForShopListEnv, Any] = + ingredientsEndpoint( + path="ingredients-for-shopping-list" + ).get + .in(SearchParams.query) + .in(PaginationParams.query) + .out(jsonBody[SearchResp[IngredientsForShoppingListResp]]) + .errorOut(oneOf(serverErrorVariant)) + .zSecuredServerLogic(searchForShoppingListHandler) + +private def searchForShoppingListHandler( + searchParams: SearchParams, + paginationParams: PaginationParams, +): ZIO[AuthenticatedUser & SearchForShopListEnv, + InternalServerError, + SearchResp[IngredientsForShoppingListResp]] = + import db.QuillConfig.ctx.* + for + dataSource <- ZIO.service[DataSource] + userId <- ZIO.serviceWith[AuthenticatedUser](_.userId) + allIngredientsAvailability <- run( + IngredientsQueries.visibleIngredientsQ(lift(userId)) + .leftJoin(quillQuery[DbShoppingList]) + .on((i, ri) => i.id == ri.ingredientId && ri.ownerId == lift(userId)) + .map((i, ri) => IngredientsForShoppingListResp(i.id, i.name, ri.map(_.ownerId).isDefined)) + ).provideDS(using dataSource) + .map(Vector.from) + .orElseFail(InternalServerError()) + res = Searchable.search(allIngredientsAvailability, searchParams) + yield SearchResp(res.paginate(paginationParams), res.length) From 7a6e8a49e425d6674a8916cc21c3d6803e3b3766 Mon Sep 17 00:00:00 2001 From: Vadim Ksenofontov Date: Mon, 21 Jul 2025 22:21:55 +0300 Subject: [PATCH 2/2] refactor: a little bit of rearrangement in quill query --- src/main/scala/api/ingredients/SearchForShoppingList.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/api/ingredients/SearchForShoppingList.scala b/src/main/scala/api/ingredients/SearchForShoppingList.scala index 947a47b0..fd43418e 100644 --- a/src/main/scala/api/ingredients/SearchForShoppingList.scala +++ b/src/main/scala/api/ingredients/SearchForShoppingList.scala @@ -46,9 +46,9 @@ private def searchForShoppingListHandler( userId <- ZIO.serviceWith[AuthenticatedUser](_.userId) allIngredientsAvailability <- run( IngredientsQueries.visibleIngredientsQ(lift(userId)) - .leftJoin(quillQuery[DbShoppingList]) - .on((i, ri) => i.id == ri.ingredientId && ri.ownerId == lift(userId)) - .map((i, ri) => IngredientsForShoppingListResp(i.id, i.name, ri.map(_.ownerId).isDefined)) + .leftJoin(quillQuery[DbShoppingList].filter(_.ownerId == lift(userId))) + .on((i, si) => i.id == si.ingredientId) + .map((i, si) => IngredientsForShoppingListResp(i.id, i.name, si.map(_.ownerId).isDefined)) ).provideDS(using dataSource) .map(Vector.from) .orElseFail(InternalServerError())