Skip to content

Commit

Permalink
Add photos support to service (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmvpm authored Apr 20, 2024
2 parents a284651 + c74efa9 commit 449112c
Show file tree
Hide file tree
Showing 19 changed files with 304 additions and 73 deletions.
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ create table offers
name text not null,
price integer not null,
description text not null,
status text not null
status text not null,
source text default null
);
create table users
Expand All @@ -49,6 +50,19 @@ create table user_offers
offer_id uuid primary key references offers (id),
user_id uuid not null references users (id)
);
create table photos
(
id uuid primary key,
url text default null,
blob bytea default null
);
create table offer_photos
(
photo_id uuid primary key references photos (id),
offer_id uuid not null references offers (id)
);
```

### Authorization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.github.mmvpm.model._
import io.circe._
import io.circe.generic.semiauto._

import java.net.{URI, URL}
import java.util.UUID

case object CirceInstances {
Expand All @@ -15,20 +16,20 @@ case object CirceInstances {
implicit val decoderUUID: Decoder[UUID] = Decoder[String].map(UUID.fromString)
implicit val encoderUUID: Encoder[UUID] = Encoder[String].contramap(_.toString)

implicit val decoderURL: Decoder[URL] = Decoder[String].map(new URI(_).toURL)
implicit val encoderURL: Encoder[URL] = Encoder[String].contramap(_.toString)

// model

implicit val decoderUserStatus: Decoder[UserStatus] = Decoder.decodeEnumeration(UserStatus)
implicit val encoderUserStatus: Encoder[UserStatus] = Encoder.encodeEnumeration(UserStatus)
implicit val codecUserStatus: Codec[UserStatus] = Codec.codecForEnumeration(UserStatus)

implicit val codecUser: Codec[User] = deriveCodec

implicit val decoderUser: Decoder[User] = deriveDecoder
implicit val encoderUser: Encoder[User] = deriveEncoder
implicit val codecOfferStatus: Codec[OfferStatus] = Codec.codecForEnumeration(OfferStatus)

implicit val decoderOfferStatus: Decoder[OfferStatus] = Decoder.decodeEnumeration(OfferStatus)
implicit val encoderOfferStatus: Encoder[OfferStatus] = Encoder.encodeEnumeration(OfferStatus)
implicit val codecOfferDescription: Codec[OfferDescription] = deriveCodec

implicit val decoderOfferDescription: Decoder[OfferDescription] = deriveDecoder
implicit val encoderOfferDescription: Encoder[OfferDescription] = deriveEncoder
implicit val codecPhoto: Codec[Photo] = deriveCodec

implicit val decoderOffer: Decoder[Offer] = deriveDecoder
implicit val encoderOffer: Encoder[Offer] = deriveEncoder
implicit val codecOffer: Codec[Offer] = deriveCodec
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ class StateManagerImpl[F[_]: MonadCancelThrow](ofsManager: OfsManager[F]) extend

private def getOffers(maxLength: Int): Seq[Offer] =
(0 until Random.nextInt(maxLength)).map { index =>
Offer(UUID.randomUUID, UUID.randomUUID, OfferDescription("name", 228, "description"), OfferStatus.Active, None)
Offer(UUID.randomUUID, UUID.randomUUID, OfferDescription("n", index, "d"), OfferStatus.Active, None, Seq.empty)
}

private def toMyOffer(current: State)(implicit message: Message): F[State] = {
Expand Down
3 changes: 2 additions & 1 deletion common/src/main/scala/com/github/mmvpm/model/Offer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ case class Offer(
userId: UserID,
description: OfferDescription,
status: OfferStatus,
source: Option[String]
source: Option[String],
photos: Seq[Photo]
)
5 changes: 5 additions & 0 deletions common/src/main/scala/com/github/mmvpm/model/Photo.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.mmvpm.model

import java.net.URL

case class Photo(id: PhotoID, url: Option[URL], blob: Option[Array[Byte]])
1 change: 1 addition & 0 deletions common/src/main/scala/com/github/mmvpm/model/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.util.UUID
package object model {
type OfferID = UUID
type UserID = UUID
type PhotoID = UUID
type Session = UUID
type Money = Int
}
12 changes: 12 additions & 0 deletions service/src/main/resources/db.migration/V3__photos.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
create table photos
(
id uuid primary key,
url text default null,
blob bytea default null
);

create table offer_photos
(
photo_id uuid primary key references photos (id),
offer_id uuid not null references offers (id)
);
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ package com.github.mmvpm.service.api

import cats.Applicative
import com.github.mmvpm.model.{OfferID, UserID}
import com.github.mmvpm.service.api.request.{CreateOfferRequest, GetOffersRequest, UpdateOfferRequest}
import com.github.mmvpm.service.api.request.{
AddOfferPhotosRequest,
CreateOfferRequest,
GetOffersRequest,
UpdateOfferRequest
}
import com.github.mmvpm.service.api.response.{OfferResponse, OffersResponse, OkResponse}
import com.github.mmvpm.service.api.support.{ApiErrorSupport, AuthSessionSupport}
import com.github.mmvpm.service.api.util.CirceInstances._
Expand Down Expand Up @@ -72,8 +77,35 @@ class OfferHandler[F[_]: Applicative](offerService: OfferService[F], override va
.out(jsonBody[OkResponse])
.serverLogic(userId => offerId => offerService.deleteOffer(userId, offerId).value)

private val addOfferPhotos: ServerEndpoint[Any, F] =
endpoint.withApiErrors.withSession.put
.summary("Add photos to the offer")
.in("api" / "v1" / "offer" / path[OfferID]("offer-id") / "photo")
.in(jsonBody[AddOfferPhotosRequest])
.out(jsonBody[OfferResponse])
.serverLogic(userId => { case (offerId, request) =>
offerService.addPhotos(userId, offerId, request).value
})

private val deleteAllOfferPhotos: ServerEndpoint[Any, F] =
endpoint.withApiErrors.withSession.delete
.summary("Delete all photos from the offer")
.in("api" / "v1" / "offer" / path[OfferID]("offer-id") / "photo" / "all")
.out(jsonBody[OkResponse])
.serverLogic(userId => offerId => offerService.deleteAllPhotos(userId, offerId).value)

override def endpoints: List[ServerEndpoint[Any, F]] =
List(getOffer, getOffersByIds, getOffers, getMyOffers, createOffer, updateOffer, deleteOffer).map(
List(
getOffer,
getOffersByIds,
getOffers,
getMyOffers,
createOffer,
updateOffer,
deleteOffer,
addOfferPhotos,
deleteAllOfferPhotos
).map(
_.withTag("offer")
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.mmvpm.service.api.request

import java.net.URL

case class AddOfferPhotosRequest(photoUrls: Seq[URL])
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.github.mmvpm.service.api.response._
import io.circe._
import io.circe.generic.semiauto._

import java.net.{URI, URL}
import java.util.UUID

case object CirceInstances {
Expand All @@ -17,54 +18,46 @@ case object CirceInstances {
implicit val decoderUUID: Decoder[UUID] = Decoder[String].map(UUID.fromString)
implicit val encoderUUID: Encoder[UUID] = Encoder[String].contramap(_.toString)

implicit val decoderURL: Decoder[URL] = Decoder[String].map(new URI(_).toURL)
implicit val encoderURL: Encoder[URL] = Encoder[String].contramap(_.toString)

// model

implicit val decoderUserStatus: Decoder[UserStatus] = Decoder.decodeEnumeration(UserStatus)
implicit val encoderUserStatus: Encoder[UserStatus] = Encoder.encodeEnumeration(UserStatus)
implicit val codecUserStatus: Codec[UserStatus] = Codec.codecForEnumeration(UserStatus)

implicit val codecUser: Codec[User] = deriveCodec

implicit val decoderUser: Decoder[User] = deriveDecoder
implicit val encoderUser: Encoder[User] = deriveEncoder
implicit val codecOfferStatus: Codec[OfferStatus] = Codec.codecForEnumeration(OfferStatus)

implicit val decoderOfferStatus: Decoder[OfferStatus] = Decoder.decodeEnumeration(OfferStatus)
implicit val encoderOfferStatus: Encoder[OfferStatus] = Encoder.encodeEnumeration(OfferStatus)
implicit val codecOfferDescription: Codec[OfferDescription] = deriveCodec

implicit val decoderOfferDescription: Decoder[OfferDescription] = deriveDecoder
implicit val encoderOfferDescription: Encoder[OfferDescription] = deriveEncoder
implicit val codecPhoto: Codec[Photo] = deriveCodec

implicit val decoderOffer: Decoder[Offer] = deriveDecoder
implicit val encoderOffer: Encoder[Offer] = deriveEncoder
implicit val codecOffer: Codec[Offer] = deriveCodec

// requests

implicit val decoderUpdateOfferRequest: Decoder[UpdateOfferRequest] = deriveDecoder
implicit val encoderUpdateOfferRequest: Encoder[UpdateOfferRequest] = deriveEncoder
implicit val codecUpdateOfferRequest: Codec[UpdateOfferRequest] = deriveCodec

implicit val codecSignUpRequest: Codec[SignUpRequest] = deriveCodec

implicit val decoderSignUpRequest: Decoder[SignUpRequest] = deriveDecoder
implicit val encoderSignUpRequest: Encoder[SignUpRequest] = deriveEncoder
implicit val codecCreateOfferRequest: Codec[CreateOfferRequest] = deriveCodec

implicit val decoderCreateOfferRequest: Decoder[CreateOfferRequest] = deriveDecoder
implicit val encoderCreateOfferRequest: Encoder[CreateOfferRequest] = deriveEncoder
implicit val codecGetOffersRequest: Codec[GetOffersRequest] = deriveCodec

implicit val decoderGetOffersRequest: Decoder[GetOffersRequest] = deriveDecoder
implicit val encoderGetOffersRequest: Encoder[GetOffersRequest] = deriveEncoder
implicit val codecAddOfferPhotosRequest: Codec[AddOfferPhotosRequest] = deriveCodec

// responses

implicit val decoderOkResponse: Decoder[OkResponse] = deriveDecoder
implicit val encoderOkResponse: Encoder[OkResponse] = deriveEncoder
implicit val codecOkResponse: Codec[OkResponse] = deriveCodec

implicit val decoderSessionResponse: Decoder[SessionResponse] = deriveDecoder
implicit val encoderSessionResponse: Encoder[SessionResponse] = deriveEncoder
implicit val codecSessionResponse: Codec[SessionResponse] = deriveCodec

implicit val decoderUserResponse: Decoder[UserResponse] = deriveDecoder
implicit val encoderUserResponse: Encoder[UserResponse] = deriveEncoder
implicit val codecUserResponse: Codec[UserResponse] = deriveCodec

implicit val decoderUserIdResponse: Decoder[UserIdResponse] = deriveDecoder
implicit val encoderUserIdResponse: Encoder[UserIdResponse] = deriveEncoder
implicit val codecUserIdResponse: Codec[UserIdResponse] = deriveCodec

implicit val decoderOffersResponse: Decoder[OffersResponse] = deriveDecoder
implicit val encoderOffersResponse: Encoder[OffersResponse] = deriveEncoder
implicit val codecOffersResponse: Codec[OffersResponse] = deriveCodec

implicit val decoderOfferResponse: Decoder[OfferResponse] = deriveDecoder
implicit val encoderOfferResponse: Encoder[OfferResponse] = deriveEncoder
implicit val codecOfferResponse: Codec[OfferResponse] = deriveCodec
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ import com.github.mmvpm.service.api.request._
import com.github.mmvpm.service.api.response._
import sttp.tapir.Schema

import java.net.URL

object SchemaInstances {

// common

implicit val schemaURL: Schema[URL] =
Schema.string.description("url").encodedExample("https://ya.ru")

// model

implicit val schemaOfferStatus: Schema[OfferStatus] =
Expand All @@ -17,6 +24,9 @@ object SchemaInstances {
implicit val schemaOfferDescription: Schema[OfferDescription] =
Schema.derived.description("User-defined offer fields")

implicit val schemaPhoto: Schema[Photo] =
Schema.derived.description("Photo")

implicit val schemaOffer: Schema[Offer] =
Schema.derived.description("Offer")

Expand All @@ -40,6 +50,9 @@ object SchemaInstances {
implicit val schemaGetOffersRequest: Schema[GetOffersRequest] =
Schema.derived.description("Request to get offers by their ids")

implicit val schemaAddOfferPhotosRequest: Schema[AddOfferPhotosRequest] =
Schema.derived.description("Request to add photos to the offer")

// responses

implicit val schemaOkResponse: Schema[OkResponse] =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.mmvpm.service.dao.offer

import cats.data.EitherT
import com.github.mmvpm.model.{Offer, OfferID, UserID}
import com.github.mmvpm.model.{Offer, OfferID, Photo, UserID}
import com.github.mmvpm.service.dao.error.OfferDaoError
import com.github.mmvpm.service.dao.schema.OfferPatch

Expand All @@ -11,4 +11,6 @@ trait OfferDao[F[_]] {
def getOffersByUser(userId: UserID): EitherT[F, OfferDaoError, List[Offer]]
def createOffer(offer: Offer): EitherT[F, OfferDaoError, Unit]
def updateOffer(userId: UserID, offerId: OfferID, patch: OfferPatch): EitherT[F, OfferDaoError, Unit]
def addPhotos(userId: UserID, offerId: OfferID, photos: Seq[Photo]): EitherT[F, OfferDaoError, Unit]
def deleteAllPhotos(userId: UserID, offerId: OfferID): EitherT[F, OfferDaoError, Unit]
}
Loading

0 comments on commit 449112c

Please sign in to comment.