Skip to content

Commit

Permalink
fix comments from PR review and remove Either
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivoyaa committed May 1, 2024
1 parent 5291009 commit 250c156
Show file tree
Hide file tree
Showing 27 changed files with 166 additions and 194 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ To launch tests that require postgres ensure you have a `docker` daemon running

Use `sbt test` to launch the tests.

You can launch the application by moving to one of the examples' directories (with `cd distage-example-bifunctor-tf` for example) and starting the following command.
You can launch the application with the following command.

```
./launcher -u scene:managed :leaderboard
Expand Down
6 changes: 0 additions & 6 deletions distage-example-monofunctor-tf/launcher

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ import scala.annotation.unused
* curl -X GET http://localhost:8080/ladder
* }}}
*/
final class LadderRole[F[+_]: Applicative](
final class LadderRole[F[_]: Applicative](
@unused ladderApi: LadderApi[F],
@unused runningServer: HttpServer,
log: LogIO[F],
) extends RoleService[F[_]] {
override def start(roleParameters: RawEntrypointParams, freeArgs: Vector[String]): Lifecycle[F[_], Unit] = {
) extends RoleService[F] {
override def start(roleParameters: RawEntrypointParams, freeArgs: Vector[String]): Lifecycle[F, Unit] = {
Lifecycle.liftF(log.info("Ladder API started!"))
}
}
Expand All @@ -60,12 +60,12 @@ object LadderRole extends RoleDescriptor {
* curl -X GET http://localhost:8080/profile/50753a00-5e2e-4a2f-94b0-e6721b0a3cc4
* }}}
*/
final class ProfileRole[F[+_]: Applicative](
final class ProfileRole[F[_]: Applicative](
@unused profileApi: ProfileApi[F],
@unused runningServer: HttpServer,
log: LogIO[F],
) extends RoleService[F[_]] {
override def start(roleParameters: RawEntrypointParams, freeArgs: Vector[String]): Lifecycle[F[_], Unit] = {
) extends RoleService[F] {
override def start(roleParameters: RawEntrypointParams, freeArgs: Vector[String]): Lifecycle[F, Unit] = {
Lifecycle.liftF(log.info("Profile API started!"))
}
}
Expand Down Expand Up @@ -96,12 +96,12 @@ object ProfileRole extends RoleDescriptor {
* curl -X GET http://localhost:8080/profile/50753a00-5e2e-4a2f-94b0-e6721b0a3cc4
* }}}
*/
final class LeaderboardRole[F[+_]: Applicative](
final class LeaderboardRole[F[_]: Applicative](
@unused ladderRole: LadderRole[F],
@unused profileRole: ProfileRole[F],
log: LogIO[F],
) extends RoleService[F[_]] {
override def start(roleParameters: RawEntrypointParams, freeArgs: Vector[String]): Lifecycle[F[_], Unit] = {
) extends RoleService[F] {
override def start(roleParameters: RawEntrypointParams, freeArgs: Vector[String]): Lifecycle[F, Unit] = {
Lifecycle.liftF(log.info("Ladder & Profile APIs started!"))
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package leaderboard.api
import org.http4s.HttpRoutes

trait HttpApi[F[_]] {
def http: HttpRoutes[F[_]]
def http: HttpRoutes[F]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,22 @@ import org.http4s.circe.*
import org.http4s.dsl.Http4sDsl

final class LadderApi[F[_]: MonadThrow](
dsl: Http4sDsl[F[_]],
dsl: Http4sDsl[F],
ladder: Ladder[F],
) extends HttpApi[F] {

import dsl.*

override def http: HttpRoutes[F[_]] = {
override def http: HttpRoutes[F] = {
HttpRoutes.of {
case GET -> Root / "ladder" =>
Ok(for {
resEither <- ladder.getScores
res <- resEither.liftTo[F]
res <- ladder.getScores
} yield res.asJson)

case POST -> Root / "ladder" / UUIDVar(userId) / LongVar(score) =>
Ok(for {
resEither <- ladder.submitScore(userId, score)
_ <- resEither.liftTo[F]
_ <- ladder.submitScore(userId, score)
} yield ())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,20 @@ import org.http4s.HttpRoutes
import org.http4s.circe.*
import org.http4s.dsl.Http4sDsl

final class ProfileApi[F[+_]: Concurrent](
dsl: Http4sDsl[F[_]],
final class ProfileApi[F[_]: Concurrent](
dsl: Http4sDsl[F],
profiles: Profiles[F],
ranks: Ranks[F],
log: LogIO[F],
) extends HttpApi[F] {

import dsl.*

override def http: HttpRoutes[F[_]] = {
override def http: HttpRoutes[F] = {
HttpRoutes.of {
case GET -> Root / "profile" / UUIDVar(userId) =>
Ok(for {
resEither <- ranks.getRank(userId)
res <- resEither.liftTo[F]
res <- ranks.getRank(userId)
} yield res.asJson)

case rq @ POST -> Root / "profile" / UUIDVar(userId) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ final case class HttpServer(

object HttpServer {

final class Impl[F[+_]](
final class Impl[F[_]](
allHttpApis: Set[HttpApi[F]]
)(implicit
async: Async[F[_]]
) extends Lifecycle.Of[F[_], HttpServer](
async: Async[F]
) extends Lifecycle.Of[F, HttpServer](
Lifecycle.fromCats {
val combinedApis = allHttpApis.map(_.http).toList.foldK

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ object LeaderboardPlugin extends PluginDef {
include(modules.prodConfigs)

object modules {
def roles[F[+_]: TagK]: RoleModuleDef = new RoleModuleDef {
def roles[F[_]: TagK]: RoleModuleDef = new RoleModuleDef {
// The `ladder` role
makeRole[LadderRole[F]]

Expand All @@ -40,10 +40,10 @@ object LeaderboardPlugin extends PluginDef {
makeRole[LeaderboardRole[F]]

// Add bundled roles: `help` & `configwriter`
include(BundledRolesModule[F[_]](version = "1.0.0"))
include(BundledRolesModule[F](version = "1.0.0"))
}

def api[F[+_]: TagK]: ModuleDef = new ModuleDef {
def api[F[_]: TagK]: ModuleDef = new ModuleDef {
// The `ladder` API
make[LadderApi[F]]
// The `profile` API
Expand All @@ -58,25 +58,25 @@ object LeaderboardPlugin extends PluginDef {

make[Ranks[F]].from[Ranks.Impl[F]]

makeTrait[Http4sDsl[F[_]]]
makeTrait[Http4sDsl[F]]
}

def repoDummy[F[+_]: TagK]: ModuleDef = new ModuleDef {
def repoDummy[F[_]: TagK]: ModuleDef = new ModuleDef {
tag(Repo.Dummy)

make[Ladder[F]].fromResource[Ladder.Dummy[F]]
make[Profiles[F]].fromResource[Profiles.Dummy[F]]
}

def repoProd[F[+_]: TagK]: ModuleDef = new ModuleDef {
def repoProd[F[_]: TagK]: ModuleDef = new ModuleDef {
tag(Repo.Prod)

make[Ladder[F]].fromResource[Ladder.Postgres[F]]
make[Profiles[F]].fromResource[Profiles.Postgres[F]]

make[SQL[F]].from[SQL.Impl[F]]

make[Transactor[F[_]]].fromResource[TransactorResource[F[_]]]
make[Transactor[F]].fromResource[TransactorResource[F]]
make[PortCheck].from(new PortCheck(3.seconds))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,37 @@ import cats.implicits.*
import distage.Lifecycle
import doobie.postgres.implicits.*
import doobie.syntax.string.*
import leaderboard.model.{QueryFailure, Score, UserId}
import leaderboard.model.{Score, UserId}
import leaderboard.sql.SQL
import logstage.LogIO

import scala.collection.concurrent.TrieMap

trait Ladder[F[_]] {
def submitScore(userId: UserId, score: Score): F[Either[QueryFailure, Unit]]
def getScores: F[Either[QueryFailure, List[(UserId, Score)]]]
def submitScore(userId: UserId, score: Score): F[Unit]
def getScores: F[List[(UserId, Score)]]
}

object Ladder {
final class Dummy[F[+_] : Applicative]
extends Lifecycle.LiftF[F[_], Ladder[F]](for {
final class Dummy[F[_]: Applicative]
extends Lifecycle.LiftF[F, Ladder[F]](for {
state <- Applicative[F].pure(TrieMap.empty[UserId, Score])
} yield {
new Ladder[F] {
override def submitScore(userId: UserId, score: Score): F[Either[Nothing, Unit]] =
Applicative[F].pure(Right(state.update(userId, score)))
override def submitScore(userId: UserId, score: Score): F[Unit] =
Applicative[F].pure(state.update(userId, score))

override def getScores: F[Either[Nothing, List[(UserId, Score)]]] =
Applicative[F].pure(Right(state.toList.sortBy(_._2)(Ordering[Score].reverse)))
override def getScores: F[List[(UserId, Score)]] =
Applicative[F].pure(state.toList.sortBy(_._2)(Ordering[Score].reverse))
}
})

final class Postgres[F[+_]: Monad](
final class Postgres[F[_]: Monad](
sql: SQL[F],
log: LogIO[F],
) extends Lifecycle.LiftF[F[_], Ladder[F]](for {
_ <- log.info(s"Creating Ladder table")
_ <- sql.execute("ladder-ddl") {
) extends Lifecycle.LiftF[F, Ladder[F]](for {
_ <- log.info(s"Creating Ladder table")
_ <- sql.execute("ladder-ddl") {
sql"""create table if not exists ladder (
| user_id uuid not null,
| score bigint not null,
Expand All @@ -44,16 +44,16 @@ object Ladder {
|""".stripMargin.update.run
}
res = new Ladder[F] {
override def submitScore(userId: UserId, score: Score): F[Either[QueryFailure, Unit]] =
override def submitScore(userId: UserId, score: Score): F[Unit] =
sql
.execute("submit-score") {
sql"""insert into ladder (user_id, score) values ($userId, $score)
|on conflict (user_id) do update set
| score = excluded.score
|""".stripMargin.update.run
}.map(_.map(_ => ()))
}.void

override val getScores: F[Either[QueryFailure, List[(UserId, Score)]]] =
override val getScores: F[List[(UserId, Score)]] =
sql.execute("get-leaderboard") {
sql"""select user_id, score from ladder order by score DESC
|""".stripMargin.query[(UserId, Score)].to[List]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,35 @@ import cats.{Applicative, Monad}
import cats.implicits.*
import doobie.postgres.implicits.*
import doobie.syntax.string.*
import leaderboard.model.{QueryFailure, UserId, UserProfile}
import leaderboard.model.{UserId, UserProfile}
import leaderboard.sql.SQL
import logstage.LogIO

import scala.collection.concurrent.TrieMap

trait Profiles[F[_]] {
def setProfile(userId: UserId, profile: UserProfile): F[Either[QueryFailure, Unit]]
def getProfile(userId: UserId): F[Either[QueryFailure, Option[UserProfile]]]
def setProfile(userId: UserId, profile: UserProfile): F[Unit]
def getProfile(userId: UserId): F[Option[UserProfile]]
}

object Profiles {
final class Dummy[F[+_] : Applicative]
extends Lifecycle.LiftF[F[_], Profiles[F]](for {
final class Dummy[F[_]: Applicative]
extends Lifecycle.LiftF[F, Profiles[F]](for {
state <- Applicative[F].pure(TrieMap.empty[UserId, UserProfile])
} yield {
new Profiles[F] {
override def setProfile(userId: UserId, profile: UserProfile): F[Either[Nothing, Unit]] =
Applicative[F].pure(Right(state.update(userId, profile)))
override def setProfile(userId: UserId, profile: UserProfile): F[Unit] =
Applicative[F].pure(state.update(userId, profile))

override def getProfile(userId: UserId): F[Either[Nothing, Option[UserProfile]]] =
Applicative[F].pure(Right(state.get(userId)))
override def getProfile(userId: UserId): F[Option[UserProfile]] =
Applicative[F].pure(state.get(userId))
}
})

final class Postgres[F[+_]: Monad](
final class Postgres[F[_]: Monad](
sql: SQL[F],
log: LogIO[F],
) extends Lifecycle.LiftF[F[_], Profiles[F]](for {
) extends Lifecycle.LiftF[F, Profiles[F]](for {
_ <- log.info("Creating Profile table")
_ <- sql.execute("ddl-profiles") {
sql"""create table if not exists profiles (
Expand All @@ -45,7 +45,7 @@ object Profiles {
|""".stripMargin.update.run
}
} yield new Profiles[F] {
override def setProfile(userId: UserId, profile: UserProfile): F[Either[QueryFailure, Unit]] = {
override def setProfile(userId: UserId, profile: UserProfile): F[Unit] = {
sql
.execute("set-profile") {
sql"""insert into profiles (user_id, name, description)
Expand All @@ -54,10 +54,10 @@ object Profiles {
| name = excluded.name,
| description = excluded.description
|""".stripMargin.update.run
}.map(_.map(_ => ()))
}.void
}

override def getProfile(userId: UserId): F[Either[QueryFailure, Option[UserProfile]]] = {
override def getProfile(userId: UserId): F[Option[UserProfile]] = {
sql.execute("get-profile") {
sql"""select name, description from profiles
|where user_id = $userId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,34 @@ package leaderboard.services

import cats.MonadThrow
import cats.implicits.*
import leaderboard.model.{QueryFailure, RankedProfile, UserId}
import leaderboard.model.{RankedProfile, UserId}
import leaderboard.repo.{Ladder, Profiles}

trait Ranks[F[_]] {
def getRank(userId: UserId): F[Either[QueryFailure, Option[RankedProfile]]]
def getRank(userId: UserId): F[Option[RankedProfile]]
}

object Ranks {
final class Impl[F[+_]: MonadThrow](
final class Impl[F[_]: MonadThrow](
ladder: Ladder[F],
profiles: Profiles[F],
) extends Ranks[F] {

override def getRank(userId: UserId): F[Either[QueryFailure, Option[RankedProfile]]] = {
override def getRank(userId: UserId): F[Option[RankedProfile]] =
for {
maybeProfileEither <- profiles.getProfile(userId)
scoresEither <- ladder.getScores
res = maybeProfileEither.map2(scoresEither) {
case (maybeProfile, scores) =>
for {
profile <- maybeProfile
rank = scores.indexWhere(_._1 == userId) + 1
score = scores.find(_._1 == userId).map(_._2)
} yield RankedProfile(
name = profile.name,
description = profile.description,
rank = rank,
score = score.getOrElse(0),
)
}
maybeProfile <- profiles.getProfile(userId)
scores <- ladder.getScores
res = for {
profile <- maybeProfile
rank = scores.indexWhere(_._1 == userId) + 1
score = scores.find(_._1 == userId).map(_._2)
} yield RankedProfile(
name = profile.name,
description = profile.description,
rank = rank,
score = score.getOrElse(0),
)
} yield res
}
}

}
Loading

0 comments on commit 250c156

Please sign in to comment.