simple routes mapped to room service

This commit is contained in:
efim 2023-04-28 09:28:57 +04:00
parent 8098f24552
commit e1364c9b9b
3 changed files with 76 additions and 17 deletions

View File

@ -27,16 +27,17 @@ trait Auth[F[_]] {
*/
def joinRoom(roomId: RoomID, playerId: PlayerID): F[ResponseCookie]
def deleteSession(sessionId: Long): F[Unit]
def deleteSession(sessionId: Long): F[ResponseCookie]
}
object Auth {
type SessionsMap = Map[Long, (RoomID, PlayerID)]
class SimpleAuth[F[_]: Sync](sessions: Ref[F, SessionsMap]) extends Auth[F] {
// TODO make "remove session usable from authed route"
val authcookieName = "authcookie"
val authcookieName = "authcookie"
class SimpleAuth[F[_]: Sync](sessions: Ref[F, SessionsMap]) extends Auth[F] {
override def joinRoom(roomId: RoomID, playerId: PlayerID): F[ResponseCookie] = {
// TODO check for existing session for same room
@ -67,8 +68,16 @@ object Auth {
}
}
override def deleteSession(sessionId: Long): F[Unit] = {
sessions.update(_.removed(sessionId))
override def deleteSession(sessionId: Long): F[ResponseCookie] = {
sessions
.update(_.removed(sessionId))
.as(
ResponseCookie(
name = authcookieName,
content = "",
secure = true
).clearCookie
)
}
}

View File

@ -35,17 +35,23 @@ object MyHttpService {
wsb.build(send, receive)
}
case GET -> Root / "vote" / vote as (playerId, roomId) => {
// TODO forward these to the service implementation
IO(println(s">> got $vote from $playerId in $roomId")) >> Ok()
IO(println(s">> got $vote from $playerId in $roomId")) >>
roomService.acceptVote(roomId, playerId, vote) >> Ok()
}
case GET -> Root / "end-voting" as (playerId, roomId) => {
IO(println(s">> got request to end voting from $playerId in $roomId")) >> Ok()
IO(println(s">> got request to end voting from $playerId in $roomId")) >>
roomService.endVoting(roomId, playerId) >> Ok()
}
case GET -> Root / "new-poll" as (playerId, roomId) => {
IO(println(s">> got request to start new voting from $playerId in $roomId")) >> Ok()
IO(println(s">> got request to start new voting from $playerId in $roomId")) >>
roomService.startNewPoll(roomId, playerId) >> Ok()
}
case GET -> Root / "logout" as (playerId, roomId) => {
IO(println(s">> got request to logout from $playerId in $roomId")) >> Ok()
for {
_ <- IO(println(s">> got request to logout from $playerId in $roomId"))
_ <- roomService.leaveRoom(roomId, playerId)
cookie = ResponseCookie(name = Auth.authcookieName, content = "").clearCookie
} yield (Response(Status.Ok).addCookie(cookie))
}
}

View File

@ -16,13 +16,17 @@ enum RoomError {
trait RoomService[F[_]] {
def createRoom(newRoom: Room): F[Either[RoomError, Room]]
def updateRoom(room: Room): F[Unit]
def updateRoom(roomId: RoomID, roomUpd: Room => Room): F[Unit]
def joinRoom(
id: RoomID,
nickName: String,
nickPassword: String,
roomPassword: String
): F[Either[RoomError, PlayerID]]
def acceptVote(roomID: RoomID, playerID: PlayerID, vote: String): F[Unit]
def endVoting(roomID: RoomID, playerID: PlayerID): F[Unit]
def startNewPoll(roomID: RoomID, playerID: PlayerID): F[Unit]
def leaveRoom(roomID: RoomID, playerID: PlayerID): F[Unit]
def deleteRoom(roomID: RoomID): F[Unit]
def getRoom(roomID: RoomID): F[Option[Room]]
def subscribe(roomID: RoomID): Stream[F, Room]
@ -44,20 +48,60 @@ class InMemoryRoomService[F[_]: Concurrent](stateRef: Ref[F, Map[RoomID, (Room,
}
} yield room
}
override def updateRoom(room: Room): F[Unit] = {
override def updateRoom(roomId: RoomID, roomUpd: Room => Room): F[Unit] = {
for {
// modify is function to update state and compute auxillary value to return, here - topic
topic <- stateRef.modify[Topic[F, Room]] { state =>
state.get(room.id) match {
case Some((oldRoom, topic)) => state.updated(room.id, (room, topic)) -> topic
publishUpd <- stateRef.modify[F[Unit]] { state =>
state.get(roomId) match {
case Some((oldRoom, topic)) =>
val newRoom = roomUpd(oldRoom)
state.updated(roomId, (newRoom, topic)) -> topic.publish1(newRoom).void
case None =>
throw new IllegalStateException(s"updateRoom with ${room.id} on nonexistent room")
throw new IllegalStateException(s"updateRoom with $roomId on nonexistent room")
}
}
_ <- topic.publish1(room) // update and publish are not atomic, sadly races can happen
_ <-
publishUpd // update and publish are not atomic, sadly races can happen (TODO use atomic ref)
} yield ()
}
override def acceptVote(roomID: RoomID, playerID: PlayerID, vote: String): F[Unit] =
updateRoom(
roomID,
room =>
room.round match {
case RoundState.Viewing(_) => room
case RoundState.Voting(votes) =>
if (room.allowedCards.contains(vote))
room.copy(round = RoundState.Voting(votes.updated(playerID, vote)))
else room
}
)
// TODO check permission
override def endVoting(roomID: RoomID, playerID: PlayerID): F[Unit] = updateRoom(
roomID,
room =>
room.round match {
case RoundState.Viewing(votes) => room.copy(round = RoundState.Viewing(votes))
case RoundState.Voting(_) => room.copy(round = RoundState.Voting(Map.empty))
}
)
override def startNewPoll(roomID: RoomID, playerID: PlayerID): F[Unit] = updateRoom(
roomID,
room =>
room.round match {
case RoundState.Viewing(_) => room.copy(round = RoundState.Voting(Map.empty))
case RoundState.Voting(votes) => room.copy(round = RoundState.Viewing(votes))
}
)
/** removes player from the active players keeps information on nick password, if one was present
*/
override def leaveRoom(roomID: RoomID, playerID: PlayerID): F[Unit] = updateRoom(
roomID,
room => room.copy(players = room.players.filterNot(_.id == playerID))
)
override def deleteRoom(roomID: RoomID): F[Unit] = {
for {
topic <- stateRef.modify[Topic[F, Room]](state =>