diff --git a/phoenix-scala/phoenix/app/phoenix/failures/ProductReviewFailures.scala b/phoenix-scala/phoenix/app/phoenix/failures/ProductReviewFailures.scala index 9e4e9c03f5..4de5d01d06 100644 --- a/phoenix-scala/phoenix/app/phoenix/failures/ProductReviewFailures.scala +++ b/phoenix-scala/phoenix/app/phoenix/failures/ProductReviewFailures.scala @@ -9,9 +9,13 @@ object ProductReviewFailures { override def description: String = s"Cannot update deleted review: $id" } - case class ProductReviewUserMismatch(id: ProductReview#Id) extends Failure { + case class UpdateProductReviewUserMismatch(id: ProductReview#Id) extends Failure { override def description: String = - s"Cannot update review $id: Only user who created the review can modify it." + s"Cannot update review $id: Only the user who created the review can modify it." } + case class FetchProductReviewUserMismatch(id: ProductReview#Id) extends Failure { + override def description: String = + s"Cannot fetch review $id: You can only fetch your own reviews." + } } diff --git a/phoenix-scala/phoenix/app/phoenix/models/review/ProductReview.scala b/phoenix-scala/phoenix/app/phoenix/models/review/ProductReview.scala index c696347f4f..27ef57b6cd 100644 --- a/phoenix-scala/phoenix/app/phoenix/models/review/ProductReview.scala +++ b/phoenix-scala/phoenix/app/phoenix/models/review/ProductReview.scala @@ -10,6 +10,8 @@ import core.utils.Validation import phoenix.utils.aliases.Json import core.db._ import core.db.ExPostgresDriver.api._ +import phoenix.models.account.User +import phoenix.models.inventory.{Sku, Skus} case class ProductReview(id: Int = 0, scope: LTree, @@ -41,8 +43,11 @@ object ProductReviews extends FoxTableQuery[ProductReview, ProductReviews](new ProductReviews(_)) with ReturningId[ProductReview, ProductReviews] { - def findOneByUserAndSku(userId: Int, skuId: Int): DBIO[Option[ProductReview]] = + def findOneByUserAndSku(userId: User#Id, skuId: Sku#Id): DBIO[Option[ProductReview]] = filter(_.userId === userId).filter(_.skuId === skuId).result.headOption + def findAllWithSkusByUser(userId: User#Id): DBIO[Seq[(ProductReview, Sku)]] = + filter(_.userId === userId).join(Skus).on(_.skuId === _.id).result + val returningLens: Lens[ProductReview, Int] = lens[ProductReview].id } diff --git a/phoenix-scala/phoenix/app/phoenix/responses/ProductReviewResponses.scala b/phoenix-scala/phoenix/app/phoenix/responses/ProductReviewResponses.scala index 32d289a6d8..0efd6cb800 100644 --- a/phoenix-scala/phoenix/app/phoenix/responses/ProductReviewResponses.scala +++ b/phoenix-scala/phoenix/app/phoenix/responses/ProductReviewResponses.scala @@ -3,10 +3,16 @@ package phoenix.responses import phoenix.models.review.ProductReview import phoenix.utils.aliases.Json -object ProductReviewResponses { +case class ProductReviewResponse(id: Int, sku: String, userId: Int, attributes: Json) extends ResponseItem + +object ProductReviewResponse { def build(review: ProductReview, skuCode: String): ProductReviewResponse = ProductReviewResponse(review.id, skuCode, review.userId, review.content) +} - case class ProductReviewResponse(id: Int, sku: String, userId: Int, attributes: Json) extends ResponseItem +case class ProductReviewsResponse(reviews: Seq[ProductReviewResponse]) extends ResponseItem +object ProductReviewsResponse { + def build(reviews: Seq[ProductReviewResponse]): ProductReviewsResponse = + ProductReviewsResponse(reviews) } diff --git a/phoenix-scala/phoenix/app/phoenix/routes/Customer.scala b/phoenix-scala/phoenix/app/phoenix/routes/Customer.scala index 51d68e8da7..6c8a3444c0 100644 --- a/phoenix-scala/phoenix/app/phoenix/routes/Customer.scala +++ b/phoenix-scala/phoenix/app/phoenix/routes/Customer.scala @@ -328,6 +328,16 @@ object Customer { ProductReviewManager.createProductReview(auth.account.id, payload) } } ~ + (get & pathEnd) { + getOrFailures { + ProductReviewManager.getAllReviewsForCustomer(auth.account.id) + } + } ~ + (get & path(IntNumber) & pathEnd) { reviewId ⇒ + getOrFailures { + ProductReviewManager.getReviewForCustomer(auth.account.id, reviewId) + } + } ~ (path(IntNumber) & patch & entity(as[UpdateProductReviewPayload]) & pathEnd) { (reviewId, payload) ⇒ mutateOrFailures { diff --git a/phoenix-scala/phoenix/app/phoenix/services/review/ProductReviewManager.scala b/phoenix-scala/phoenix/app/phoenix/services/review/ProductReviewManager.scala index b3fa5c57da..58d11444f5 100644 --- a/phoenix-scala/phoenix/app/phoenix/services/review/ProductReviewManager.scala +++ b/phoenix-scala/phoenix/app/phoenix/services/review/ProductReviewManager.scala @@ -2,15 +2,14 @@ package phoenix.services.review import java.time.Instant +import core.db._ import phoenix.failures.ProductReviewFailures._ import phoenix.models.account.{Scope, User} import phoenix.models.inventory.Skus import phoenix.models.review.{ProductReview, ProductReviews} import phoenix.payloads.ProductReviewPayloads._ -import phoenix.responses.ProductReviewResponses -import phoenix.responses.ProductReviewResponses.ProductReviewResponse +import phoenix.responses.{ProductReviewResponse, ProductReviewsResponse} import phoenix.utils.aliases._ -import core.db._ object ProductReviewManager { @@ -19,7 +18,25 @@ object ProductReviewManager { for { review ← * <~ ProductReviews.mustFindById404(reviewId) sku ← * <~ Skus.mustFindById404(review.skuId) - } yield ProductReviewResponses.build(review, sku.code) + } yield ProductReviewResponse.build(review, sku.code) + + def getReviewForCustomer( + userId: User#Id, + reviewId: ProductReview#Id)(implicit ec: EC, oc: OC, db: DB): DbResultT[ProductReviewResponse] = + for { + review ← * <~ ProductReviews.mustFindById404(reviewId) + sku ← * <~ Skus.mustFindById404(review.skuId) + _ ← * <~ failIf(userId != review.userId, FetchProductReviewUserMismatch(reviewId)) + } yield ProductReviewResponse.build(review, sku.code) + + def getAllReviewsForCustomer( + userId: User#Id)(implicit ec: EC, oc: OC, db: DB): DbResultT[ProductReviewsResponse] = + for { + reviewsWithSkus ← * <~ ProductReviews.findAllWithSkusByUser(userId) + reviewResponses = reviewsWithSkus.map { + case (review, sku) ⇒ ProductReviewResponse.build(review, sku.code) + } + } yield ProductReviewsResponse(reviewResponses) def createProductReview(userId: User#Id, payload: CreateProductReviewPayload)( implicit ec: EC, @@ -29,16 +46,16 @@ object ProductReviewManager { for { scope ← * <~ Scope.resolveOverride(payload.scope) sku ← * <~ Skus.mustFindByCode(payload.sku) - productReview ← * <~ ProductReviews - .findOneByUserAndSku(userId, sku.id) - .findOrCreate( - ProductReviews.create( - ProductReview(scope = scope, - content = payload.attributes, - userId = userId, - skuId = sku.id))) - sku ← * <~ Skus.mustFindById404(productReview.skuId) - } yield ProductReviewResponses.build(productReview, sku.code) + review ← * <~ ProductReviews + .findOneByUserAndSku(userId, sku.id) + .findOrCreate( + ProductReviews.create( + ProductReview(scope = scope, + content = payload.attributes, + userId = userId, + skuId = sku.id))) + sku ← * <~ Skus.mustFindById404(review.skuId) + } yield ProductReviewResponse.build(review, sku.code) def updateProductReview(reviewId: ProductReview#Id, payload: UpdateProductReviewPayload)( implicit ec: EC, @@ -48,12 +65,12 @@ object ProductReviewManager { for { review ← * <~ ProductReviews.mustFindById404(reviewId) _ ← * <~ failIf(review.archivedAt.isDefined, ProductReviewIsArchived(reviewId)) - _ ← * <~ failIf(au.account.id != review.userId, ProductReviewUserMismatch(reviewId)) + _ ← * <~ failIf(au.account.id != review.userId, UpdateProductReviewUserMismatch(reviewId)) newValue ← * <~ ProductReviews.update( review, review.copy(content = payload.attributes, updatedAt = Instant.now)) sku ← * <~ Skus.mustFindById404(review.skuId) - } yield ProductReviewResponses.build(newValue, sku.code) + } yield ProductReviewResponse.build(newValue, sku.code) def archiveByContextAndId(reviewId: ProductReview#Id)(implicit ec: EC, oc: OC, db: DB): DbResultT[Unit] = for {