circe codec derivation to models
This commit is contained in:
parent
c6bfdacd1d
commit
df35f09b71
|
@ -4,7 +4,8 @@ import cats.effect._
|
||||||
import cats.syntax.all._
|
import cats.syntax.all._
|
||||||
import org.http4s._, org.http4s.dsl.io._, org.http4s.implicits._
|
import org.http4s._, org.http4s.dsl.io._, org.http4s.implicits._
|
||||||
import org.http4s.websocket.WebSocketFrame
|
import org.http4s.websocket.WebSocketFrame
|
||||||
import io.circe.generic.auto._
|
// import io.circe.generic.auto._
|
||||||
|
import io.circe.syntax._
|
||||||
import org.http4s.circe.CirceEntityDecoder._
|
import org.http4s.circe.CirceEntityDecoder._
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import org.http4s.server.websocket.WebSocketBuilder
|
import org.http4s.server.websocket.WebSocketBuilder
|
||||||
|
@ -23,9 +24,14 @@ object MyHttpService {
|
||||||
AuthedRoutes.of {
|
AuthedRoutes.of {
|
||||||
case GET -> Root / subscribe as (playerId, roomId) => {
|
case GET -> Root / subscribe as (playerId, roomId) => {
|
||||||
val send: Stream[IO, WebSocketFrame] =
|
val send: Stream[IO, WebSocketFrame] =
|
||||||
|
{
|
||||||
|
val a = Stream
|
||||||
|
.emits(TestModels.testChangesList)
|
||||||
|
.covary[IO].metered(5.second).map(state => WebSocketFrame.Text(state.asJson.noSpaces))
|
||||||
Stream
|
Stream
|
||||||
.awakeEvery[IO](1.seconds)
|
.awakeEvery[IO](1.seconds)
|
||||||
.map(_ => WebSocketFrame.Text("text"))
|
.map(_ => WebSocketFrame.Text("text"))
|
||||||
|
}
|
||||||
val receive: Pipe[IO, WebSocketFrame, Unit] = _.evalMap {
|
val receive: Pipe[IO, WebSocketFrame, Unit] = _.evalMap {
|
||||||
case WebSocketFrame.Text(text, _) => Sync[IO].delay(println(text))
|
case WebSocketFrame.Text(text, _) => Sync[IO].delay(println(text))
|
||||||
case other => Sync[IO].delay(println(other))
|
case other => Sync[IO].delay(println(other))
|
||||||
|
|
13
build.sbt
13
build.sbt
|
@ -52,16 +52,17 @@ lazy val backend = project
|
||||||
libraryDependencies += "co.fs2" %% "fs2-core" % "3.6.1",
|
libraryDependencies += "co.fs2" %% "fs2-core" % "3.6.1",
|
||||||
libraryDependencies += "org.typelevel" %% "cats-core" % "2.9.0",
|
libraryDependencies += "org.typelevel" %% "cats-core" % "2.9.0",
|
||||||
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.4.9",
|
libraryDependencies += "org.typelevel" %% "cats-effect" % "3.4.9",
|
||||||
libraryDependencies ++= Seq(
|
|
||||||
"io.circe" %% "circe-core",
|
|
||||||
"io.circe" %% "circe-generic",
|
|
||||||
"io.circe" %% "circe-parser"
|
|
||||||
).map(_ % circeVersion)
|
|
||||||
)
|
)
|
||||||
.dependsOn(common)
|
.dependsOn(common)
|
||||||
|
|
||||||
lazy val common = project
|
lazy val common = project
|
||||||
.in(file("common"))
|
.in(file("common"))
|
||||||
.settings(
|
.settings(
|
||||||
commonSettings
|
commonSettings,
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"io.circe" %% "circe-core",
|
||||||
|
"io.circe" %% "circe-generic",
|
||||||
|
"io.circe" %% "circe-parser"
|
||||||
|
).map(_ % circeVersion),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package industries.sunshine.planningpoker.common
|
package industries.sunshine.planningpoker.common
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import io.circe._
|
||||||
|
|
||||||
object Models {
|
object Models {
|
||||||
|
|
||||||
|
@ -20,58 +21,41 @@ object Models {
|
||||||
allowedCards: List[String],
|
allowedCards: List[String],
|
||||||
round: RoundState,
|
round: RoundState,
|
||||||
canCloseRound: Boolean = false
|
canCloseRound: Boolean = false
|
||||||
) {
|
) derives Codec.AsObject {
|
||||||
def playersCount: Int = players.size
|
def playersCount: Int = players.size
|
||||||
}
|
}
|
||||||
|
|
||||||
object RoomStateView {
|
given Encoder[Map[PlayerID, String]] = Encoder.encodeMap[PlayerID, String]
|
||||||
val me = Player("wormy", PlayerID(1))
|
given Decoder[Map[PlayerID, String]] = Decoder.decodeMap[PlayerID, String]
|
||||||
val testRoom = {
|
given Codec[Map[PlayerID, String]] = Codec.from(summon[Decoder[Map[PlayerID, String]]], summon[Encoder[Map[PlayerID, String]]])
|
||||||
val pony = Player("pony", PlayerID(2))
|
|
||||||
RoomStateView(
|
|
||||||
players = List(me, Player("birdy", PlayerID(3)), pony),
|
|
||||||
me = me.id,
|
|
||||||
allowedCards = List("xs", "s", "m", "l", "xl"),
|
|
||||||
round = VotingRound(myCard = Some("s"), alreadyVoted = List(me, pony)),
|
|
||||||
canCloseRound = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val testOpenedRoom = {
|
|
||||||
val pony = Player("pony", PlayerID(10))
|
|
||||||
val birdy = Player("birdy", PlayerID(11))
|
|
||||||
val horsey = Player("horsey", PlayerID(12))
|
|
||||||
RoomStateView(
|
|
||||||
players = List(me, birdy, pony, horsey),
|
|
||||||
me = me.id,
|
|
||||||
allowedCards = List("xs", "s", "m", "l", "xl"),
|
|
||||||
round = ViewingRound(
|
|
||||||
Map(me.id -> "xs", pony.id -> "l", birdy.id -> "s", horsey.id -> "m")
|
|
||||||
),
|
|
||||||
canCloseRound = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
trait RoundState
|
enum RoundState derives Codec.AsObject:
|
||||||
|
|
||||||
/** view state for round before votes are open player can know their vote and
|
/** view state for round before votes are open player can know their vote and
|
||||||
* who of the other players have voted
|
* who of the other players have voted
|
||||||
*/
|
*/
|
||||||
final case class VotingRound(
|
case Voting(
|
||||||
myCard: Option[String],
|
myCard: Option[String],
|
||||||
alreadyVoted: List[Player]
|
alreadyVoted: List[Player]
|
||||||
) extends RoundState
|
)
|
||||||
|
|
||||||
/** view state for round after opening the votes
|
/** view state for round after opening the votes
|
||||||
*/
|
*/
|
||||||
final case class ViewingRound(
|
case Viewing(
|
||||||
votes: Map[PlayerID, String]
|
votes: Map[PlayerID, String]
|
||||||
) extends RoundState
|
)
|
||||||
|
|
||||||
final case class PlayerID(id: Long)
|
final case class PlayerID(id: Long) derives Codec.AsObject
|
||||||
final case class Player(name: String, id: PlayerID)
|
object PlayerID {
|
||||||
|
given KeyEncoder[PlayerID] with
|
||||||
|
def apply(key: PlayerID): String = key.toString
|
||||||
|
given KeyDecoder[PlayerID] with
|
||||||
|
def apply(key: String): Option[PlayerID] = key.toLongOption.map(PlayerID.apply(_))
|
||||||
|
}
|
||||||
|
|
||||||
final case class RoomID(id: Long)
|
final case class Player(name: String, id: PlayerID) derives Codec.AsObject
|
||||||
|
|
||||||
|
final case class RoomID(id: Long) derives Codec.AsObject
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package industries.sunshine.planningpoker
|
package industries.sunshine.planningpoker
|
||||||
|
|
||||||
|
import io.circe.generic.semiauto._
|
||||||
|
import io.circe._
|
||||||
|
|
||||||
object Requests {
|
object Requests {
|
||||||
final case class LogIn(roomName: String, nickname: String, password: String)
|
final case class LogIn(roomName: String, nickname: String, password: String) derives Codec.AsObject
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
package industries.sunshine.planningpoker
|
||||||
|
|
||||||
|
import industries.sunshine.planningpoker.common.Models.*
|
||||||
|
|
||||||
|
object TestModels {
|
||||||
|
val me = Player("wormy", PlayerID(1))
|
||||||
|
val pony = Player("pony", PlayerID(10))
|
||||||
|
val birdy = Player("birdy", PlayerID(11))
|
||||||
|
val horsey = Player("horsey", PlayerID(12))
|
||||||
|
|
||||||
|
val testRoom = {
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = Some("s"), alreadyVoted = List(me, pony)),
|
||||||
|
canCloseRound = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val testOpenedRoom = {
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony, horsey),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Viewing(
|
||||||
|
Map(me.id -> "xs", pony.id -> "l", birdy.id -> "s", horsey.id -> "m")
|
||||||
|
),
|
||||||
|
canCloseRound = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val testChangesList = List(
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = None, alreadyVoted = List.empty),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, pony),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = None, alreadyVoted = List.empty),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = None, alreadyVoted = List.empty),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = None, alreadyVoted = List(birdy)),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy)),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy)),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony, horsey),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy)),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony, horsey),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy, horsey)),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony, horsey),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Voting(myCard = Some("m"), alreadyVoted = List(birdy, horsey, pony)),
|
||||||
|
canCloseRound = true
|
||||||
|
),
|
||||||
|
RoomStateView(
|
||||||
|
players = List(me, birdy, pony, horsey),
|
||||||
|
me = me.id,
|
||||||
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
|
round = RoundState.Viewing(
|
||||||
|
Map(me.id -> "m", pony.id -> "l", birdy.id -> "s", horsey.id -> "m")
|
||||||
|
),
|
||||||
|
canCloseRound = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
|
@ -23,7 +23,7 @@ object Main {
|
||||||
myId: Option[PlayerID]
|
myId: Option[PlayerID]
|
||||||
)
|
)
|
||||||
// TODO is this ok for state creation? link with auth component and use in another?
|
// TODO is this ok for state creation? link with auth component and use in another?
|
||||||
val appStateSignal = Var(AppState(Some(RoomStateView.me.id))).signal
|
val appStateSignal = Var(AppState(Some(TestModels.me.id))).signal
|
||||||
|
|
||||||
def appElement(): Element = {
|
def appElement(): Element = {
|
||||||
div(
|
div(
|
||||||
|
@ -32,7 +32,7 @@ object Main {
|
||||||
className := "h-24 w-full flex flex-for justify-center items-center bg-green-200",
|
className := "h-24 w-full flex flex-for justify-center items-center bg-green-200",
|
||||||
p(className := "text-2xl", "Here be header"),
|
p(className := "text-2xl", "Here be header"),
|
||||||
),
|
),
|
||||||
RoomView.renderRoom(RoomStateView.testRoom)
|
RoomView.renderRoom(TestModels.testRoom)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,11 @@ object TableView {
|
||||||
myId: Option[PlayerID]
|
myId: Option[PlayerID]
|
||||||
): CardState = {
|
): CardState = {
|
||||||
state match {
|
state match {
|
||||||
case isOpen: VotingRound =>
|
case isOpen: RoundState.Voting =>
|
||||||
if (myId.forall(_ == id)) {
|
if (myId.forall(_ == id)) {
|
||||||
isOpen.myCard.fold(NoCard(name))(vote => Open(vote))
|
isOpen.myCard.fold(NoCard(name))(vote => Open(vote))
|
||||||
} else isOpen.alreadyVoted.find(_.id == id).fold(NoCard(name))(_ => CardBack)
|
} else isOpen.alreadyVoted.find(_.id == id).fold(NoCard(name))(_ => CardBack)
|
||||||
case isClosed: ViewingRound =>
|
case isClosed: RoundState.Viewing =>
|
||||||
isClosed.votes
|
isClosed.votes
|
||||||
.get(id)
|
.get(id)
|
||||||
.fold {
|
.fold {
|
||||||
|
|
Loading…
Reference in New Issue