dummy Auth and authed routes
This commit is contained in:
parent
c501d14094
commit
c6bfdacd1d
|
@ -0,0 +1,74 @@
|
||||||
|
package industries.sunshine.planningpoker
|
||||||
|
|
||||||
|
import cats.effect._
|
||||||
|
import cats.data.Kleisli
|
||||||
|
import cats.data.OptionT
|
||||||
|
import org.http4s.Request
|
||||||
|
import industries.sunshine.planningpoker.common.Models.PlayerID
|
||||||
|
import java.util.UUID
|
||||||
|
import industries.sunshine.planningpoker.common.Models.RoomID
|
||||||
|
|
||||||
|
trait Auth {
|
||||||
|
|
||||||
|
/** for middleware that converts sessionId into PlayerId
|
||||||
|
*/
|
||||||
|
def authUser: Kleisli[[A] =>> cats.data.OptionT[cats.effect.IO, A], Request[
|
||||||
|
cats.effect.IO
|
||||||
|
], (PlayerID, RoomID)]
|
||||||
|
|
||||||
|
/** Get sessionId for accessing a room
|
||||||
|
* @param roomPassword
|
||||||
|
* \- requirement to get access to the room
|
||||||
|
*/
|
||||||
|
def accessRoom(
|
||||||
|
roomName: String,
|
||||||
|
roomPassword: String,
|
||||||
|
nickName: String
|
||||||
|
): IO[Either[Unit, Long]]
|
||||||
|
|
||||||
|
def leaveRoom(
|
||||||
|
sessionId: Long
|
||||||
|
): IO[Unit]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object Auth {
|
||||||
|
def pureBadStub = new Auth {
|
||||||
|
override def authUser =
|
||||||
|
Kleisli((req: Request[IO]) =>
|
||||||
|
OptionT.liftF(IO(println(s"authUser: $req")) >> IO(PlayerID(14) -> RoomID(101)))
|
||||||
|
)
|
||||||
|
override def accessRoom(
|
||||||
|
roomName: String,
|
||||||
|
roomPassword: String,
|
||||||
|
nickName: String
|
||||||
|
) =
|
||||||
|
IO(println(s"> access room for $roomName $roomPassword $nickName, to return stub 111")) >> IO.pure(Right(111L))
|
||||||
|
|
||||||
|
override def leaveRoom(sessionId: Long): IO[Unit] =
|
||||||
|
IO(s"got request to leave for $sessionId")
|
||||||
|
}
|
||||||
|
|
||||||
|
type SessionsMap = Map[Long, (RoomID, PlayerID)]
|
||||||
|
val sessionsRef = Ref.of[IO, SessionsMap](Map.empty)
|
||||||
|
val roomPasswordsRef = Ref.of[IO, Map[Long, String]](Map.empty)
|
||||||
|
|
||||||
|
def apply(): IO[Auth] =
|
||||||
|
for {
|
||||||
|
store <- sessionsRef
|
||||||
|
roomPasswords <- roomPasswordsRef
|
||||||
|
} yield new Auth {
|
||||||
|
override def authUser = Kleisli { (req: Request[IO]) =>
|
||||||
|
{
|
||||||
|
???
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override def accessRoom(
|
||||||
|
roomName: String,
|
||||||
|
roomPassword: String,
|
||||||
|
nickName: String
|
||||||
|
): IO[Either[Unit, Long]] = ???
|
||||||
|
|
||||||
|
override def leaveRoom(sessionId: Long): IO[Unit] = ???
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,53 +2,27 @@ package industries.sunshine.planningpoker
|
||||||
|
|
||||||
import cats.effect._
|
import cats.effect._
|
||||||
import cats.syntax.all._
|
import cats.syntax.all._
|
||||||
import org.http4s._, org.http4s.dsl.io._, org.http4s.implicits._
|
|
||||||
import com.comcast.ip4s._
|
import com.comcast.ip4s._
|
||||||
import org.http4s.HttpRoutes
|
|
||||||
import org.http4s.dsl.io._
|
|
||||||
import org.http4s.implicits._
|
|
||||||
import org.http4s.ember.server._
|
import org.http4s.ember.server._
|
||||||
import org.http4s.websocket.WebSocketFrame
|
|
||||||
|
|
||||||
import io.circe.generic.auto._
|
|
||||||
import org.http4s.circe.CirceEntityDecoder._
|
|
||||||
import scala.concurrent.duration._
|
|
||||||
import org.http4s.server.websocket.WebSocketBuilder
|
|
||||||
import fs2._
|
import fs2._
|
||||||
|
|
||||||
object BackendApp extends IOApp.Simple {
|
object BackendApp extends IOApp.Simple {
|
||||||
|
|
||||||
def service(wsb: WebSocketBuilder[IO]) = HttpRoutes
|
|
||||||
.of[IO] {
|
|
||||||
case req @ POST -> Root / login => {
|
|
||||||
for {
|
|
||||||
data <- req.as[Requests.LogIn]
|
|
||||||
resp <- Ok(s"got some: ${data}")
|
|
||||||
} yield resp
|
|
||||||
}
|
|
||||||
case GET -> Root / subscribe => {
|
|
||||||
val send: Stream[IO, WebSocketFrame] =
|
|
||||||
Stream.awakeEvery[IO](1.seconds).map(_ => WebSocketFrame.Text("text"))
|
|
||||||
val receive: Pipe[IO, WebSocketFrame, Unit] = _.evalMap {
|
|
||||||
case WebSocketFrame.Text(text, _) => Sync[IO].delay(println(text))
|
|
||||||
case other => Sync[IO].delay(println(other))
|
|
||||||
}
|
|
||||||
wsb.build(send, receive)
|
|
||||||
}
|
|
||||||
case _ => Ok("hello")
|
|
||||||
}
|
|
||||||
.orNotFound
|
|
||||||
|
|
||||||
override def run: IO[Unit] = {
|
override def run: IO[Unit] = {
|
||||||
val a = 1
|
val host = host"0.0.0.0"
|
||||||
IO.println(s"Hello, World! $a") >>
|
val port = port"8080"
|
||||||
EmberServerBuilder
|
|
||||||
|
val server = for {
|
||||||
|
srv <- EmberServerBuilder
|
||||||
.default[IO]
|
.default[IO]
|
||||||
.withHost(ipv4"0.0.0.0")
|
.withHost(host)
|
||||||
.withPort(port"8080")
|
.withPort(port)
|
||||||
.withHttpWebSocketApp(service)
|
.withHttpWebSocketApp(MyHttpService.create(Auth.pureBadStub)(_))
|
||||||
.build
|
.build
|
||||||
.use(_ => IO.never)
|
} yield srv
|
||||||
.as(ExitCode.Success)
|
|
||||||
|
server.use(server =>
|
||||||
|
IO.delay(println(s"Server Has Started at ${server.address}")) >> IO.never.as(ExitCode.Success))
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
package industries.sunshine.planningpoker
|
||||||
|
|
||||||
|
import cats.effect._
|
||||||
|
import cats.syntax.all._
|
||||||
|
import org.http4s._, org.http4s.dsl.io._, org.http4s.implicits._
|
||||||
|
import org.http4s.websocket.WebSocketFrame
|
||||||
|
import io.circe.generic.auto._
|
||||||
|
import org.http4s.circe.CirceEntityDecoder._
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import org.http4s.server.websocket.WebSocketBuilder
|
||||||
|
import fs2._
|
||||||
|
import industries.sunshine.planningpoker.common.Models.RoomID
|
||||||
|
import industries.sunshine.planningpoker.common.Models.PlayerID
|
||||||
|
import org.http4s.server.AuthMiddleware.apply
|
||||||
|
import org.http4s.server.AuthMiddleware
|
||||||
|
|
||||||
|
object MyHttpService {
|
||||||
|
def create(auth: Auth)(
|
||||||
|
wsb: WebSocketBuilder[cats.effect.IO]
|
||||||
|
): HttpApp[cats.effect.IO] = {
|
||||||
|
|
||||||
|
val authedRoomRoutes: AuthedRoutes[(PlayerID, RoomID), IO] =
|
||||||
|
AuthedRoutes.of {
|
||||||
|
case GET -> Root / subscribe as (playerId, roomId) => {
|
||||||
|
val send: Stream[IO, WebSocketFrame] =
|
||||||
|
Stream
|
||||||
|
.awakeEvery[IO](1.seconds)
|
||||||
|
.map(_ => WebSocketFrame.Text("text"))
|
||||||
|
val receive: Pipe[IO, WebSocketFrame, Unit] = _.evalMap {
|
||||||
|
case WebSocketFrame.Text(text, _) => Sync[IO].delay(println(text))
|
||||||
|
case other => Sync[IO].delay(println(other))
|
||||||
|
}
|
||||||
|
wsb.build(send, receive)
|
||||||
|
}
|
||||||
|
case GET -> Root / "vote" / vote as (playerId, roomId) => {
|
||||||
|
// TODO forward these to the service implementation
|
||||||
|
Ok(s">> got $vote from $playerId in $roomId")
|
||||||
|
}
|
||||||
|
case GET -> Root / "end-voting" as (playerId, roomId) => {
|
||||||
|
Ok(s">> got request to end voting from $playerId in $roomId")
|
||||||
|
}
|
||||||
|
case GET -> Root / "new-poll" as (playerId, roomId) => {
|
||||||
|
Ok(s">> got request to start new voting from $playerId in $roomId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val authMiddleware = AuthMiddleware(auth.authUser)
|
||||||
|
val aa = authMiddleware(authedRoomRoutes)
|
||||||
|
|
||||||
|
val authenticationRoute = HttpRoutes
|
||||||
|
.of[IO] {
|
||||||
|
case req @ POST -> Root / login => {
|
||||||
|
for {
|
||||||
|
data <- req.as[Requests.LogIn]
|
||||||
|
authResult <- auth.accessRoom(
|
||||||
|
data.roomName,
|
||||||
|
data.password,
|
||||||
|
data.nickname
|
||||||
|
)
|
||||||
|
resp <- authResult match {
|
||||||
|
case Left(error) =>
|
||||||
|
Forbidden(error)
|
||||||
|
case Right(sessionId) => {
|
||||||
|
Ok("Logged in!").map(
|
||||||
|
_.addCookie(
|
||||||
|
ResponseCookie("authcookie", sessionId.toString())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} yield resp
|
||||||
|
}
|
||||||
|
case _ => Ok("hello")
|
||||||
|
}
|
||||||
|
|
||||||
|
(authenticationRoute <+> authMiddleware(authedRoomRoutes)).orNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,7 +3,8 @@ package industries.sunshine.planningpoker.common
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
object Models {
|
object Models {
|
||||||
/** view of the single planning poker round
|
|
||||||
|
/** view of the single planning poker round
|
||||||
* @param players
|
* @param players
|
||||||
* \- people who are currently playing
|
* \- people who are currently playing
|
||||||
* @param allowedCards
|
* @param allowedCards
|
||||||
|
@ -13,22 +14,22 @@ object Models {
|
||||||
* @param canCloseRound
|
* @param canCloseRound
|
||||||
* \- whether current player has access to button to finish the round
|
* \- whether current player has access to button to finish the round
|
||||||
*/
|
*/
|
||||||
final case class RoomStateView(
|
final case class RoomStateView(
|
||||||
players: List[Player],
|
players: List[Player],
|
||||||
me: PlayerID,
|
me: PlayerID,
|
||||||
allowedCards: List[String],
|
allowedCards: List[String],
|
||||||
round: RoundState,
|
round: RoundState,
|
||||||
canCloseRound: Boolean = false
|
canCloseRound: Boolean = false
|
||||||
) {
|
) {
|
||||||
def playersCount: Int = players.size
|
def playersCount: Int = players.size
|
||||||
}
|
}
|
||||||
|
|
||||||
object RoomStateView {
|
object RoomStateView {
|
||||||
val me = Player("wormy", PlayerID())
|
val me = Player("wormy", PlayerID(1))
|
||||||
val testRoom = {
|
val testRoom = {
|
||||||
val pony = Player("pony", PlayerID())
|
val pony = Player("pony", PlayerID(2))
|
||||||
RoomStateView(
|
RoomStateView(
|
||||||
players = List(me, Player("birdy", PlayerID()), pony),
|
players = List(me, Player("birdy", PlayerID(3)), pony),
|
||||||
me = me.id,
|
me = me.id,
|
||||||
allowedCards = List("xs", "s", "m", "l", "xl"),
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
round = VotingRound(myCard = Some("s"), alreadyVoted = List(me, pony)),
|
round = VotingRound(myCard = Some("s"), alreadyVoted = List(me, pony)),
|
||||||
|
@ -36,37 +37,41 @@ object RoomStateView {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val testOpenedRoom = {
|
val testOpenedRoom = {
|
||||||
val pony = Player("pony", PlayerID())
|
val pony = Player("pony", PlayerID(10))
|
||||||
val birdy = Player("birdy", PlayerID())
|
val birdy = Player("birdy", PlayerID(11))
|
||||||
val horsey = Player("horsey", PlayerID())
|
val horsey = Player("horsey", PlayerID(12))
|
||||||
RoomStateView(
|
RoomStateView(
|
||||||
players = List(me, birdy, pony, horsey),
|
players = List(me, birdy, pony, horsey),
|
||||||
me = me.id,
|
me = me.id,
|
||||||
allowedCards = List("xs", "s", "m", "l", "xl"),
|
allowedCards = List("xs", "s", "m", "l", "xl"),
|
||||||
round = ViewingRound(Map( me.id -> "xs", pony.id -> "l", birdy.id -> "s", horsey.id -> "m" )),
|
round = ViewingRound(
|
||||||
|
Map(me.id -> "xs", pony.id -> "l", birdy.id -> "s", horsey.id -> "m")
|
||||||
|
),
|
||||||
canCloseRound = true
|
canCloseRound = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trait RoundState
|
trait RoundState
|
||||||
|
|
||||||
/** 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(
|
final case class VotingRound(
|
||||||
myCard: Option[String],
|
myCard: Option[String],
|
||||||
alreadyVoted: List[Player]
|
alreadyVoted: List[Player]
|
||||||
) extends RoundState
|
) extends RoundState
|
||||||
|
|
||||||
/** view state for round after opening the votes
|
/** view state for round after opening the votes
|
||||||
*/
|
*/
|
||||||
final case class ViewingRound(
|
final case class ViewingRound(
|
||||||
votes: Map[PlayerID, String]
|
votes: Map[PlayerID, String]
|
||||||
) extends RoundState
|
) extends RoundState
|
||||||
|
|
||||||
final class PlayerID
|
final case class PlayerID(id: Long)
|
||||||
final case class Player(name: String, id: PlayerID)
|
final case class Player(name: String, id: PlayerID)
|
||||||
|
|
||||||
|
final case class RoomID(id: Long)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue