diff --git a/build.sbt b/build.sbt index 135a09f..fcbf622 100644 --- a/build.sbt +++ b/build.sbt @@ -75,6 +75,7 @@ lazy val model = project lazy val db = project .in(file("modules/db")) + .dependsOn(model) .settings(sharedSettings) .settings(testSettings) .settings(name := s"${gsProjectName.value}-db") @@ -86,6 +87,7 @@ lazy val db = project lazy val api = project .in(file("modules/api")) + .dependsOn(model, db) .settings(sharedSettings) .settings(testSettings) .settings(name := s"${gsProjectName.value}-api") diff --git a/modules/db/src/main/scala/gs/smolban/db/DbError.scala b/modules/db/src/main/scala/gs/smolban/db/DbError.scala new file mode 100644 index 0000000..5d3652c --- /dev/null +++ b/modules/db/src/main/scala/gs/smolban/db/DbError.scala @@ -0,0 +1,20 @@ +package gs.smolban.db + +import gs.smolban.model.SmolbanError +import gs.smolban.model.Ticket + +/** Parent type of all database errors in Smolban. + */ +sealed trait DbError extends SmolbanError + +object DbError: + + /** Returned if an attempt is made to insert a [[Ticket]], where the Ticket ID + * already exists for the specified [[gs.smolban.model.Group]]. + * + * @param ref + * The Ticket Reference that was in conflict. + */ + case class TicketAlreadyExists(ref: Ticket.Reference) extends DbError + +end DbError diff --git a/modules/db/src/main/scala/gs/smolban/db/TicketDb.scala b/modules/db/src/main/scala/gs/smolban/db/TicketDb.scala new file mode 100644 index 0000000..e636622 --- /dev/null +++ b/modules/db/src/main/scala/gs/smolban/db/TicketDb.scala @@ -0,0 +1,48 @@ +package gs.smolban.db + +import cats.data.EitherT +import gs.smolban.model.Ticket + +trait TicketDb[F[_]]: + /** Create a new [[Ticket]] in the database. + * + * @param ticket + * The [[Ticket]] to store. + * @return + * The [[Ticket]] that was created, or an error. + */ + def createTicket(ticket: Ticket): EitherT[F, DbError, Ticket] + + /** Read the specified [[Ticket]] from the database. + * + * @param ref + * The reference to the [[Ticket]] to retrieve. + * @return + * The value of the [[Ticket]], or `None` if no such [[Ticket]] exists. + */ + def readTicket(ref: Ticket.Reference): F[Option[Ticket]] + + /** Given a reference to a [[Ticket]] and some new value for that [[Ticket]], + * update the [[Ticket]] in the database. + * + * @param ref + * The refrence to the [[Ticket]] to update. + * @param newValue + * The new values with which to overwrite the [[Ticket]]. + * @return + * The modified [[Ticket]] if the operation succeeded, or `None` if the + * given reference does not exist. + */ + def updateTicket( + ref: Ticket.Reference, + newValue: Ticket + ): F[Option[Ticket]] + + /** Delete the specified [[Ticket]]. + * + * @param ref + * The reference to the [[Ticket]] to delete. + * @return + * `true` if the operation deleted a [[Ticket]], `false` otherwise. + */ + def deleteTicket(ref: Ticket.Reference): F[Boolean] diff --git a/modules/db/src/main/scala/gs/smolban/db/doobie/DoobieTicketDb.scala b/modules/db/src/main/scala/gs/smolban/db/doobie/DoobieTicketDb.scala new file mode 100644 index 0000000..0f2a9c5 --- /dev/null +++ b/modules/db/src/main/scala/gs/smolban/db/doobie/DoobieTicketDb.scala @@ -0,0 +1,27 @@ +package gs.smolban.db.doobie + +import cats.data.EitherT +import cats.effect.Async +import gs.smolban.db.DbError +import gs.smolban.db.TicketDb +import gs.smolban.model.Ticket + +final class DoobieTicketDb[F[_]: Async] extends TicketDb[F]: + /** @inheritdoc + */ + override def createTicket(ticket: Ticket): EitherT[F, DbError, Ticket] = ??? + + /** @inheritdoc + */ + override def readTicket(ref: Ticket.Reference): F[Option[Ticket]] = ??? + + /** @inheritdoc + */ + override def updateTicket( + ref: Ticket.Reference, + newValue: Ticket + ): F[Option[Ticket]] = ??? + + /** @inheritdoc + */ + override def deleteTicket(ref: Ticket.Reference): F[Boolean] = ??? diff --git a/modules/model/src/main/scala/gs/smolban/model/SmolbanError.scala b/modules/model/src/main/scala/gs/smolban/model/SmolbanError.scala new file mode 100644 index 0000000..80c8089 --- /dev/null +++ b/modules/model/src/main/scala/gs/smolban/model/SmolbanError.scala @@ -0,0 +1,5 @@ +package gs.smolban.model + +/** The parent trait for all errors in Smolban. + */ +trait SmolbanError diff --git a/modules/model/src/main/scala/gs/smolban/model/Ticket.scala b/modules/model/src/main/scala/gs/smolban/model/Ticket.scala index 504019e..87b95e1 100644 --- a/modules/model/src/main/scala/gs/smolban/model/Ticket.scala +++ b/modules/model/src/main/scala/gs/smolban/model/Ticket.scala @@ -38,10 +38,23 @@ case class Ticket( status: Ticket.Status, statusHistory: List[(Ticket.Status, Instant)], assignee: Option[User.Id] -) +): + lazy val reference: Ticket.Reference = Ticket.Reference(id, group) object Ticket: + /** Composite reference that uniquely addresses any [[Ticket]]. + * + * @param id + * The ticket's unique identifier within the [[Group]]. + * @param group + * The unique identifier of the [[Group]]. + */ + case class Reference( + id: Ticket.Id, + group: Group.Id + ) + /** Unique identifier - relative to some [[Group]] - for a [[Ticket]]. This is * an opaque type for a Long. In general, [[Ticket]] identifiers are * sequences within a group. diff --git a/project/build.properties b/project/build.properties index 081fdbb..ee4c672 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.0 +sbt.version=1.10.1