This commit is contained in:
Pat Garrity 2024-08-08 21:37:25 -05:00
parent 1195bab53c
commit 41e9e797c1
Signed by: pfm
GPG key ID: 5CA5D21BAB7F3A76
5 changed files with 119 additions and 25 deletions

View file

@ -38,10 +38,10 @@ val Deps = new {
} }
val Gs = new { val Gs = new {
val Uuid: ModuleID = "gs" %% "gs-uuid-v0" % "0.2.4" val Uuid: ModuleID = "gs" %% "gs-uuid-v0" % "0.3.0"
val Slug: ModuleID = "gs" %% "gs-slug-v0" % "0.1.3" val Slug: ModuleID = "gs" %% "gs-slug-v0" % "0.1.3"
val Config: ModuleID = "gs" %% "gs-config-v0" % "0.1.1" val Config: ModuleID = "gs" %% "gs-config-v0" % "0.1.1"
val Datagen: ModuleID = "gs" %% "gs-datagen-core-v0" % "0.1.1" val Datagen: ModuleID = "gs" %% "gs-datagen-core-v0" % "0.2.0"
} }
val MUnit: ModuleID = "org.scalameta" %% "munit" % "1.0.0-RC1" val MUnit: ModuleID = "org.scalameta" %% "munit" % "1.0.0-RC1"

View file

@ -6,22 +6,28 @@ import doobie.*
import doobie.implicits.* import doobie.implicits.*
import gs.smolban.db.DbError import gs.smolban.db.DbError
import gs.smolban.db.TicketDb import gs.smolban.db.TicketDb
import gs.smolban.db.doobie.DoobieTypes.*
import gs.smolban.model.CreatedAt import gs.smolban.model.CreatedAt
import gs.smolban.model.CreatedBy import gs.smolban.model.CreatedBy
import gs.smolban.model.Group import gs.smolban.model.Group
import gs.smolban.model.Ticket import gs.smolban.model.Ticket
import gs.smolban.model.users.User import gs.smolban.model.users.User
import gs.uuid.v0.UUIDFormat
import java.util.UUID
final class DoobieTicketDb[F[_]: Async] extends TicketDb[F]: final class DoobieTicketDb[F[_]: Async](
val xa: Transactor[F]
) extends TicketDb[F]:
import DoobieTicketDb.Sql
/** @inheritdoc /** @inheritdoc
*/ */
override def createTicket(ticket: Ticket): EitherT[F, DbError, Ticket] = ??? override def createTicket(ticket: Ticket): EitherT[F, DbError, Ticket] = ???
/** @inheritdoc /** @inheritdoc
*/ */
override def readTicket(ref: Ticket.Reference): F[Option[Ticket]] = ??? override def readTicket(ref: Ticket.Reference): F[Option[Ticket]] =
???
// Sql.readTicket(ref.id, ref.group).option.transact(xa)
// for compose multiple reads
/** @inheritdoc /** @inheritdoc
*/ */
@ -36,27 +42,31 @@ final class DoobieTicketDb[F[_]: Async] extends TicketDb[F]:
object DoobieTicketDb: object DoobieTicketDb:
object Sql: private object Sql:
implicit val ticketIdGet: Get[Ticket.Id] = Get[Long].tmap(Ticket.Id(_)) case class TicketContents(
implicit val ticketIdPut: Put[Ticket.Id] = Put[Long].tcontramap(_.toLong()) createdAt: CreatedAt,
createdBy: CreatedBy,
title: String,
description: String,
status: Ticket.Status,
assignee: Option[User.Id]
)
implicit val groupIdGet: Get[Group.Id] = Get[Array[Byte]].tmap { bytes => // TODO: figure out how to read tags
Group.Id(gs.uuid.v0.UUID(gs.uuid.v0.UUIDFormat.fromBytes(bytes))) // also storing them
} // make tagdb and such...
implicit val groupIdPut: Put[Group.Id] = Put[Array[Byte]].tcontramap { id => def readTicket(
gs.uuid.v0.UUIDFormat.toBytes(id.toUUID().toUUID) ticketId: Ticket.Id,
} groupId: Group.Id
): Query0[TicketContents] = sql"""
/* private case class TicketContents( createdAt: CreatedAt, createdBy: SELECT
* CreatedBy, title: String, description: String, status: Ticket.Status, created_at, created_by, title, description, status, assignee
* assignee: Option[User.Id] ) FROM tickets
* WHERE
* def readTicket(ticketId: Ticket.Id, groupId: Group.Id) = sql""" SELECT ticket_id = $ticketId AND group_id = $groupId
* created_at, created_by, title, description, status, assignee FROM tickets """.query[TicketContents]
* WHERE ticket_id = $ticketId AND group_id = $groupId
* """.query[TicketContents] */
end Sql end Sql

View file

@ -0,0 +1,68 @@
package gs.smolban.db.doobie
import DoobieTypes.ErrorMessages
import doobie.*
import gs.smolban.model.CreatedAt
import gs.smolban.model.CreatedBy
import gs.smolban.model.Group
import gs.smolban.model.Ticket
import gs.smolban.model.users.User
import gs.uuid.v0.UUID
import gs.uuid.v0.UUIDFormat
trait DoobieTypes:
implicit val ticketIdGet: Get[Ticket.Id] =
Get[Long].tmap(Ticket.Id(_))
implicit val ticketIdPut: Put[Ticket.Id] =
Put[Long].tcontramap(_.toLong())
implicit val ticketStatusGet: Get[Ticket.Status] =
Get[String].temap(dbValue =>
Ticket.Status.parse(dbValue) match
case None => Left(ErrorMessages.invalidTicketStatus(dbValue))
case Some(status) => Right(status)
)
implicit val ticketStatusPut: Put[Ticket.Status] =
Put[String].tcontramap(_.name)
implicit val groupIdGet: Get[Group.Id] =
Get[Array[Byte]].tmap(bytes => Group.Id(UUID(UUIDFormat.fromBytes(bytes))))
implicit val groupIdPut: Put[Group.Id] =
Put[Array[Byte]].tcontramap(id => UUIDFormat.toBytes(id.toUUID().toUUID()))
implicit val userIdGet: Get[User.Id] =
Get[Array[Byte]].tmap(bytes => User.Id(UUID(UUIDFormat.fromBytes(bytes))))
implicit val userIdPut: Put[User.Id] =
Put[Array[Byte]].tcontramap(id => UUIDFormat.toBytes(id.toUUID().toUUID()))
implicit val createdAtGet: Get[CreatedAt] =
Get[Long].tmap(CreatedAt.fromMilliseconds)
implicit val createdAtPut: Put[CreatedAt] =
Put[Long].tcontramap(_.toMilliseconds())
implicit val createdByGet: Get[CreatedBy] =
Get[Array[Byte]].tmap(bytes =>
CreatedBy(User.Id(UUID(UUIDFormat.fromBytes(bytes))))
)
implicit val createdByPut: Put[CreatedBy] =
Put[Array[Byte]].tcontramap(id =>
UUIDFormat.toBytes(id.toUserId().toUUID().toUUID())
)
object DoobieTypes extends DoobieTypes:
object ErrorMessages:
def invalidTicketStatus(candidate: String): String =
s"'$candidate' is not a valid ticket status."
end ErrorMessages
end DoobieTypes

View file

@ -19,6 +19,15 @@ object CreatedAt:
*/ */
def apply(timestamp: Instant): CreatedAt = timestamp def apply(timestamp: Instant): CreatedAt = timestamp
/** Instantiate a new [[CreatedAt]].
*
* @param millis
* The epoch milliseconds describing the instant of this timestamp.
* @return
* The new instance.
*/
def fromMilliseconds(millis: Long): CreatedAt = Instant.ofEpochMilli(millis)
given CanEqual[CreatedAt, CreatedAt] = CanEqual.derived given CanEqual[CreatedAt, CreatedAt] = CanEqual.derived
given Show[CreatedAt] = _.toInstant().toString() given Show[CreatedAt] = _.toInstant().toString()
@ -31,4 +40,11 @@ object CreatedAt:
*/ */
def toInstant(): Instant = createdAt def toInstant(): Instant = createdAt
/** Express this instant as epoch milliseconds.
*
* @return
* The epoch milliseconds.
*/
def toMilliseconds(): Long = createdAt.toEpochMilli()
end CreatedAt end CreatedAt

View file

@ -7,7 +7,7 @@ import cats.Show
* *
* Tags are defined _globally_ in Smolban. * Tags are defined _globally_ in Smolban.
*/ */
opaque type Tag = String opaque type Tag = String // TODO: Make this have a unique id (long?) and value
object Tag: object Tag: