Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions src/main/scala/api/recipes/publicationRequests/Create.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import sttp.tapir.generic.auto.*
import sttp.tapir.ztapir.*
import zio.ZIO

final case class CannotPublishRecipeWithCustomIngredients(
final case class CannotPublishRecipeWithPrivateIngredients(
ingredients: Seq[IngredientId],
message: String = "Cannot publish recipe with custom ingredients",
message: String = "Cannot publish recipe with private ingredients",
)
object CannotPublishRecipeWithCustomIngredients:
val variant = BadRequest.variantJson[CannotPublishRecipeWithCustomIngredients]
object CannotPublishRecipeWithPrivateIngredients:
val variant = BadRequest.variantJson[CannotPublishRecipeWithPrivateIngredients]

final case class RecipeAlreadyPublished(
recipeId: RecipeId,
Expand Down Expand Up @@ -50,7 +50,7 @@ private val create: ZServerEndpoint[CreateEnv, Any] =
.errorOut(oneOf(
serverErrorVariant,
recipeNotFoundVariant,
CannotPublishRecipeWithCustomIngredients.variant,
CannotPublishRecipeWithPrivateIngredients.variant,
RecipeAlreadyPending.variant,
RecipeAlreadyPublished.variant,
))
Expand All @@ -60,7 +60,7 @@ private def createHandler(recipeId: RecipeId):
ZIO[
AuthenticatedUser & CreateEnv,
InternalServerError | RecipeAlreadyPublished | RecipeAlreadyPending
| CannotPublishRecipeWithCustomIngredients | RecipeNotFound,
| CannotPublishRecipeWithPrivateIngredients | RecipeNotFound,
PublicationRequestId
] =
for
Expand All @@ -81,15 +81,15 @@ private def createHandler(recipeId: RecipeId):
.when(alreadyPending)

userId <- ZIO.serviceWith[AuthenticatedUser](_.userId)
customIngredientIdsInRecipe <- run(
IngredientsQueries.customIngredientsQ(lift(userId))
privateIngredientIdsInRecipe <- run(
IngredientsQueries.privateIngredientsQ(lift(userId))
.filter(i => liftQuery(recipe.ingredients).contains(i.id))
.map(_.id)
).provideDS(using dataSource)
.orElseFail(InternalServerError())

_ <- ZIO.fail(CannotPublishRecipeWithCustomIngredients(customIngredientIdsInRecipe))
.when(customIngredientIdsInRecipe.nonEmpty)
_ <- ZIO.fail(CannotPublishRecipeWithPrivateIngredients(privateIngredientIdsInRecipe))
.when(privateIngredientIdsInRecipe.nonEmpty)

reqId <- ZIO.serviceWithZIO[RecipePublicationRequestsRepo](_
.createPublicationRequest(recipeId)
Expand Down
22 changes: 10 additions & 12 deletions src/main/scala/api/recipes/publicationRequests/GetAll.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,24 @@ package api.recipes.publicationRequests
import api.Authentication.{AuthenticatedUser, zSecuredServerLogic}
import api.EndpointErrorVariants.{recipeNotFoundVariant, serverErrorVariant}
import api.PublicationRequestStatusResp
import api.moderation.ModerationHistoryResponse
import db.repositories.{RecipePublicationRequestsRepo, RecipesRepo}
import domain.{InternalServerError, RecipeId, RecipeNotFound}

import io.circe.Decoder
import io.circe.derivation.Configuration
import io.circe.generic.auto.*
import java.time.OffsetDateTime
import sttp.tapir.generic.auto.*
import sttp.tapir.json.circe.*
import sttp.tapir.ztapir.*
import zio.ZIO

import java.time.OffsetDateTime

final case class RecipeModerationHistoryResponse(
createdAt: OffsetDateTime,
updatedAt: OffsetDateTime,
status: PublicationRequestStatusResp,
reason: Option[String]
)
createdAt: OffsetDateTime,
updatedAt: OffsetDateTime,
status: PublicationRequestStatusResp,
)

private type GetAllEnv = RecipePublicationRequestsRepo & RecipesRepo

val getAll: ZServerEndpoint[GetAllEnv, Any] =
Expand All @@ -38,9 +37,10 @@ private def getAllHandler(recipeId: RecipeId):
List[RecipeModerationHistoryResponse]
] =
for
isUserOwner <- ZIO.serviceWithZIO[RecipesRepo](_.isUserOwner(recipeId))
userIsOwner <- ZIO.serviceWithZIO[RecipesRepo](_.isUserOwner(recipeId))
.orElseFail(InternalServerError())
_ <- ZIO.fail(RecipeNotFound(recipeId)).unless(isUserOwner)
_ <- ZIO.fail(RecipeNotFound(recipeId))
.unless(userIsOwner)

dbRequests <- ZIO.serviceWithZIO[RecipePublicationRequestsRepo](_.getAllByRecipeId(recipeId))
.orElseFail(InternalServerError())
Expand All @@ -49,9 +49,7 @@ private def getAllHandler(recipeId: RecipeId):
dbReq => RecipeModerationHistoryResponse(
dbReq.createdAt, dbReq.updatedAt,
PublicationRequestStatusResp.fromDomain(dbReq.status.toDomain(dbReq.reason)),
dbReq.reason
)
)
.sortBy(_.updatedAt)

yield res
2 changes: 1 addition & 1 deletion src/main/scala/api/storages/GetAll.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package api.storages
import api.Authentication.{zSecuredServerLogic, AuthenticatedUser}
import api.EndpointErrorVariants.serverErrorVariant
import db.repositories.StoragesRepo
import domain.{UserId, StorageId, InternalServerError}
import domain.{StorageId, InternalServerError}

import io.circe.generic.auto.*
import sttp.tapir.generic.auto.*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package db.repositories

import api.Authentication.AuthenticatedUser
import api.PublicationRequestStatusResp
import api.moderation.ModerationHistoryResponse
import db.DbError
import db.tables.DbIngredient
import db.tables.publication.{DbIngredientPublicationRequest, DbPublicationRequestStatus}
import domain.{IngredientId, PublicationRequestId, PublicationRequestStatus, UserId}
import io.getquill.*

import io.getquill.*
import javax.sql.DataSource
import java.util.UUID
import zio.{IO, RLayer, ZIO, ZLayer}
Expand Down
5 changes: 4 additions & 1 deletion src/main/scala/db/repositories/IngredientsRepo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ object IngredientsQueries:
ingredientsQ.filter(_.isPublished)

inline def customIngredientsQ(inline userId: UserId): EntityQuery[DbIngredient] =
ingredientsQ.filter(i => i.ownerId.contains(userId))
ingredientsQ.filter(_.ownerId.contains(userId))

inline def privateIngredientsQ(inline userId: UserId): EntityQuery[DbIngredient] =
customIngredientsQ(userId).filter(!_.isPublished)

inline def visibleIngredientsQ(inline userId: UserId): EntityQuery[DbIngredient] =
ingredientsQ.filter(i => i.isPublished || i.ownerId.contains(userId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import io.circe.parser.decode
import zio.*
import zio.http.*
import zio.test.*
import db.repositories.IngredientsRepo
import api.recipes.publicationRequests.CannotPublishRecipeWithPrivateIngredients

object RequestRecipePublicationTests extends ZIOIntegrationTestSpec:
private def endpointPath(recipeId: RecipeId): URL =
Expand Down Expand Up @@ -58,6 +60,70 @@ object RequestRecipePublicationTests extends ZIOIntegrationTestSpec:
yield assertTrue(resp.status == Status.Created)
&& assertTrue(request.is(_.some).recipeId == recipeId)
&& assertTrue(request.is(_.some).status == DbPublicationRequestStatus.Pending)
}
},
test("When requesting publication of recipe with no ingredients, pending request should be created") {
for
user <- registerUser

recipeId <- createCustomRecipe(user, Vector.empty)

resp <- requestRecipePublication(user, recipeId)

bodyStr <- resp.body.asString
requestId <- ZIO.fromOption(bodyStr.toUUID)
request <- ZIO.serviceWithZIO[RecipePublicationRequestsRepo](_.get(requestId))
yield assertTrue(resp.status == Status.Created)
&& assertTrue(request.is(_.some).recipeId == recipeId)
&& assertTrue(request.is(_.some).status == DbPublicationRequestStatus.Pending)
},
test("""When requesting publication of recipe with published custom ingredients,
pending request should be created""") {
for
user <- registerUser

n <- Gen.int(2, 8).runHead.some
ingredientIds <- ZIO.foreach(1 to n)(_ => for
ingredientId <- createCustomIngredient(user)
_ <- ZIO.serviceWithZIO[IngredientsRepo](_.publish(ingredientId))
yield ingredientId)
recipeId <- createCustomRecipe(user, ingredientIds.toVector)

resp <- requestRecipePublication(user, recipeId)

bodyStr <- resp.body.asString
requestId <- ZIO.fromOption(bodyStr.toUUID)
request <- ZIO.serviceWithZIO[RecipePublicationRequestsRepo](_.get(requestId))
yield assertTrue(resp.status == Status.Created)
&& assertTrue(request.is(_.some).recipeId == recipeId)
&& assertTrue(request.is(_.some).status == DbPublicationRequestStatus.Pending)
},
test("""When requesting publication of recipe with private ingredients,
should get 400 Cannot publish recipe with private ingredients""") {
for
user <- registerUser

n <- Gen.int(2, 8).runHead.some
publishedCustomIngredients <- ZIO.foreach(1 to n)(_ => for
ingredientId <- createCustomIngredient(user)
_ <- ZIO.serviceWithZIO[IngredientsRepo](_.publish(ingredientId))
yield ingredientId)
m <- Gen.int(2, 8).runHead.some
publiсIngredients <- createNPublicIngredients(m)

k <- Gen.int(2, 8).runHead.some
privateIngredients <- ZIO.foreach(1 to n)(_ => createCustomIngredient(user))

ingredientIds = publishedCustomIngredients ++ publiсIngredients ++ privateIngredients

recipeId <- createCustomRecipe(user, ingredientIds.toVector)

resp <- requestRecipePublication(user, recipeId)

bodyStr <- resp.body.asString
error = decode[CannotPublishRecipeWithPrivateIngredients](bodyStr)
yield assertTrue(resp.status == Status.BadRequest)
&& assertTrue(error.isRight)
&& assertTrue(error.forall(_.ingredients hasSameElementsAs privateIngredients))
},
).provideLayer(testLayer)