Add error handling
parent
edf110e37a
commit
09146d610a
|
@ -6,6 +6,7 @@ val LogbackVersion = "1.2.3"
|
|||
val BetterFilesVersion = "3.8.0"
|
||||
val DirectoryWatcherVersion = "0.9.6"
|
||||
val ScalaLoggingVersion = "3.9.2"
|
||||
val MeowMTLVersion = "0.4.0"
|
||||
|
||||
organization := "name.orhideous"
|
||||
name := "twicher"
|
||||
|
@ -43,6 +44,7 @@ libraryDependencies ++= Seq(
|
|||
"io.methvin" %% "directory-watcher-better-files" % DirectoryWatcherVersion,
|
||||
"com.typesafe.scala-logging" %% "scala-logging" % ScalaLoggingVersion,
|
||||
"ch.qos.logback" % "logback-classic" % LogbackVersion,
|
||||
"com.olegpy" %% "meow-mtl-core" % MeowMTLVersion,
|
||||
compilerPlugin("org.typelevel" %% "kind-projector" % "0.10.3"),
|
||||
compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.0")
|
||||
)
|
||||
|
|
|
@ -4,18 +4,24 @@ import cats.effect.ExitCode
|
|||
import cats.effect.IO
|
||||
import cats.effect.IOApp
|
||||
import cats.implicits._
|
||||
import name.orhideous.twicher.error.HttpErrorHandler
|
||||
import name.orhideous.twicher.error.QuoteHttpErrorHandler
|
||||
import name.orhideous.twicher.error.TwicherError
|
||||
import name.orhideous.twicher.persistence.QuotesFileRepository
|
||||
import org.http4s.implicits._
|
||||
import org.http4s.server.Router
|
||||
import org.http4s.server.blaze._
|
||||
import com.olegpy.meow.hierarchy._
|
||||
|
||||
object Main extends IOApp {
|
||||
private val host = sys.env.getOrElse("TWICHER_HOST", "0.0.0.0")
|
||||
private val port = sys.env.getOrElse("TWICHER_PORT", "9000").toInt
|
||||
private val dir = sys.env.getOrElse("TWICHER_DIR", "./data")
|
||||
|
||||
private implicit val errorHandler: HttpErrorHandler[IO, TwicherError] = new QuoteHttpErrorHandler[IO]
|
||||
|
||||
private val repository = QuotesFileRepository[IO](dir)
|
||||
private val routes = Router[IO]("/" -> Routes[IO](repository)).orNotFound
|
||||
private val routes = Router[IO]("/" -> new Routes[IO, TwicherError](repository).routes).orNotFound
|
||||
|
||||
override def run(args: List[String]): IO[ExitCode] =
|
||||
BlazeServerBuilder[IO]
|
||||
|
|
|
@ -3,14 +3,17 @@ package name.orhideous.twicher
|
|||
import cats.effect.Sync
|
||||
import cats.implicits._
|
||||
import io.circe.generic.auto._
|
||||
import name.orhideous.twicher.persistence.QuotesAlgebra
|
||||
import org.http4s._
|
||||
import org.http4s.circe.CirceEntityEncoder._
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
|
||||
class Routes[F[_]: Sync](algebra: QuotesAlgebra[F]) extends Http4sDsl[F] {
|
||||
import name.orhideous.twicher.error.HttpErrorHandler
|
||||
import name.orhideous.twicher.persistence.QuotesAlgebra
|
||||
|
||||
val routes: HttpRoutes[F] = HttpRoutes.of[F] {
|
||||
class Routes[F[_]: Sync, E <: Throwable](algebra: QuotesAlgebra[F])(implicit H: HttpErrorHandler[F, E])
|
||||
extends Http4sDsl[F] {
|
||||
|
||||
private val rawRoutes: HttpRoutes[F] = HttpRoutes.of[F] {
|
||||
case GET -> Root =>
|
||||
algebra.list.flatMap(Ok(_))
|
||||
|
||||
|
@ -20,8 +23,5 @@ class Routes[F[_]: Sync](algebra: QuotesAlgebra[F]) extends Http4sDsl[F] {
|
|||
case GET -> Root / IntVar(id) =>
|
||||
algebra.read(id).flatMap(Ok(_))
|
||||
}
|
||||
}
|
||||
|
||||
object Routes {
|
||||
def apply[F[_]: Sync](algebra: QuotesAlgebra[F]): HttpRoutes[F] = new Routes(algebra).routes
|
||||
val routes: HttpRoutes[F] = H.handle(rawRoutes)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package name.orhideous.twicher.error
|
||||
|
||||
import org.http4s.HttpRoutes
|
||||
|
||||
trait HttpErrorHandler[F[_], E <: Throwable] {
|
||||
def handle(routes: HttpRoutes[F]): HttpRoutes[F]
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package name.orhideous.twicher.error
|
||||
|
||||
import cats.ApplicativeError
|
||||
import org.http4s.HttpRoutes
|
||||
import org.http4s.dsl.Http4sDsl
|
||||
|
||||
class QuoteHttpErrorHandler[F[_]](implicit M: ApplicativeError[F, TwicherError])
|
||||
extends HttpErrorHandler[F, TwicherError]
|
||||
with RoutesHttpErrorWrapper[F, TwicherError]
|
||||
with Http4sDsl[F] {
|
||||
|
||||
private val handler: Handler = {
|
||||
case TwicherError.NoQuotes => InternalServerError()
|
||||
case TwicherError.NoSuchQuote => NotFound()
|
||||
}
|
||||
|
||||
override def handle(routes: HttpRoutes[F]): HttpRoutes[F] =
|
||||
wrapWith(handler)(routes)
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package name.orhideous.twicher.error
|
||||
|
||||
import cats.data.Kleisli
|
||||
import cats.data.OptionT
|
||||
import cats.ApplicativeError
|
||||
import org.http4s.HttpRoutes
|
||||
import org.http4s.Request
|
||||
import org.http4s.Response
|
||||
import cats.implicits._
|
||||
|
||||
trait RoutesHttpErrorWrapper[F[_], E <: Throwable] {
|
||||
protected type Handler = E => F[Response[F]]
|
||||
|
||||
protected def wrapWith(handler: Handler)(routes: HttpRoutes[F])(implicit ev: ApplicativeError[F, E]): HttpRoutes[F] =
|
||||
Kleisli { req: Request[F] =>
|
||||
OptionT {
|
||||
routes
|
||||
.run(req)
|
||||
.value
|
||||
.handleErrorWith(handler(_).map(Option(_)))
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue