Skip to content

Instantly share code, notes, and snippets.

@hamnis
Created May 31, 2018 20:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hamnis/9299af62d1a388d36eabbf190b517460 to your computer and use it in GitHub Desktop.
Save hamnis/9299af62d1a388d36eabbf190b517460 to your computer and use it in GitHub Desktop.
package no.scalabin.http4s.directives
package routes
import cats.Monad
import linx._
import Route.{MethodPartial, ResponseDirective}
import org.http4s.{Method, Response, Status}
import scala.language.higherKinds
final class Route[F[+_]] private(val path: Linx[_, _])(pf: PartialFunction[String, ResponseDirective[F]]) extends PartialFunction[String, ResponseDirective[F]] {
override def isDefinedAt(x: String): Boolean = pf.isDefinedAt(x)
override def apply(v1: String): ResponseDirective[F] = pf.apply(v1)
}
final case class Methods[F[+_]](map: Map[Method, ResponseDirective[F]]) extends MethodPartial[F] {
override def isDefinedAt(x: Method): Boolean = map.isDefinedAt(x)
override def apply(v1: Method): ResponseDirective[F] = map(v1)
}
object Methods {
def apply[F[+_]](parts: (Method, ResponseDirective[F])*): Methods[F] =
Methods(parts.toMap)
}
object Route {
type ResponseDirective[F[+_]] = Directive[F, Response[F], Response[F]]
type MethodPartial[F[+_]] = PartialFunction[Method, ResponseDirective[F]]
def static[F[+_]: Monad](path: StaticLinx)(pf: MethodPartial[F]): Route[F] = {
new Route[F](path)({
case path() => {
for {
req <- Directive.request
res <- if (pf.isDefinedAt(req.method)) pf(req.method) else Directive.error(Response[F](Status.MethodNotAllowed))
} yield {
res
}
}
})
}
def variable[F[+_]: Monad, A](path: Linx[A, Option[A]])(f: A => MethodPartial[F]): Route[F] = {
new Route[F](path)({
case path(args) => {
val pf = f(args)
for {
req <- Directive.request
res <- if (pf.isDefinedAt(req.method)) pf(req.method) else Directive.error(Response[F](Status.MethodNotAllowed))
} yield {
res
}
}
})
}
}
package no.scalabin.http4s.directives
package routes
import cats.Monad
import cats.effect.{Effect, IO}
import fs2.{Stream, StreamApp}
import linx.StaticLinx
import Route.ResponseDirective
import org.http4s.server.blaze.BlazeBuilder
import org.http4s.{HttpService, Method}
import scala.concurrent.ExecutionContext
import scala.language.higherKinds
case class HealthCheck[F[+ _] : Monad](path: StaticLinx)(val rf: ResponseDirective[F]) {
val route: Route[F] = Route.static(path) { case Method.GET => rf }
}
case class Serve[F[+ _] : Effect](healthCheck: HealthCheck[F], port: Int) {
def stream(routes: Route[F]*)(implicit ec: ExecutionContext): Stream[F, StreamApp.ExitCode] = {
val completeRoutes = healthCheck.route :: routes.toList
val PathMapping = Plan[F]().PathMapping
val routesPF = completeRoutes.reduce[PartialFunction[String, ResponseDirective[F]]]((a, b) => a orElse b)
val service = HttpService[F](PathMapping(routesPF))
println("%-6s".format("**** Serving the following routes *****"))
completeRoutes.foreach(r => println("%s".format(r.path)))
BlazeBuilder[F]
.bindHttp(port, "localhost")
.enableHttp2(true)
.withWebSockets(true)
.mountService(service, "/")
.serve
}
}
object TestApp extends StreamApp[IO] {
import org.http4s.dsl.io._
import scala.concurrent.ExecutionContext.Implicits.global
override def stream(args: List[String], requestShutdown: IO[Unit]): Stream[IO, StreamApp.ExitCode] =
Serve(HealthCheck(linx.Root / "health")(Directive.successF(Ok("Hello Y'all"))), 1337).stream(
Route.variable(linx.Root / "so" / "hello" / 'name)(name => {
case Method.GET => Directive.successF(Ok(s"Hello $name"))
}
),
Route.variable(linx.Root / 'greeting / 'name) {
case (greeting, name) =>
Methods(
Method.GET -> Directive.successF(Ok(s"$greeting, $name")),
Method.POST -> Directive.successF(Ok(s"$greeting, $name from POST"))
)
}
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment