sup has modules for http4s, http4s-client and circe:
libraryDependencies ++= Seq(
"com.kubukoz" %% "sup-http4s" % "0.7.0",
"com.kubukoz" %% "sup-http4s-client" % "0.7.0",
"com.kubukoz" %% "sup-circe" % "0.7.0"
)
Imports:
import sup._
import sup.modules.http4s._, sup.modules.http4sclient._, sup.modules.circe._
What’s included
statusCodeHealthCheck
, healthCheckRoutes
, Encoder
/Decoder
In the http4s module, you get a router for (almost) free.
You just need some instances, a healthcheck and an EntityEncoder[F, HealthResult[H]]
.
The circe module provides circe Encoder
/Decoder
instances for all the important bits,
so you can use them together with http4s-circe
and bundle the whole thing up:
import io.circe._, org.http4s.circe.CirceEntityCodec._, org.http4s._, org.http4s.implicits._, org.http4s.client._
import cats.effect._, cats._, cats.data._, sup.data._
implicit val client: Client[IO] = Client.fromHttpApp(HttpApp.notFound[IO])
// client: Client[IO] = org.http4s.client.Client$$anon$1@14b802e4
val healthcheck: HealthCheck[IO, Id] = statusCodeHealthCheck(Request[IO]())
// healthcheck: HealthCheck[IO, Id] = sup.HealthCheck$$anon$1@5011853e
val routes = healthCheckRoutes(healthcheck)
// routes: HttpRoutes[IO] = Kleisli(
// org.http4s.HttpRoutes$$$Lambda$10492/0x000000010300f040@5f917abb
// )
//also works with reports!
val report = HealthReporter.fromChecks(
healthcheck.through(mods.tagWith("foo")),
HealthCheck.const[IO, Id](Health.Healthy).through(mods.tagWith("bar"))
)
// report: HealthReporter[IO, NonEmptyList, Tagged[String, H]] = sup.HealthCheck$$anon$1@6ede865d
val reportRoutes = healthCheckRoutes(report)
// reportRoutes: HttpRoutes[IO] = Kleisli(
// org.http4s.HttpRoutes$$$Lambda$10492/0x000000010300f040@56b04d83
// )
val responseBody = reportRoutes.orNotFound.run(Request(uri = Uri.uri("/health-check"))).flatMap(_.bodyAsText.compile.string)
// responseBody: IO[String] = Bind(
// Map(
// Suspend(org.http4s.HttpRoutes$$$Lambda$10503/0x000000010301f440@7618bfd4),
// cats.data.OptionT$$Lambda$10505/0x000000010301d840@5690c415,
// StackTrace(
// List(
// cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),
// cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),
// cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),
// cats.effect.IO.map(IO.scala:106),
// cats.effect.IOLowPriorityInstances$IOEffect.map(IO.scala:870),
// cats.effect.IOLowPriorityInstances$IOEffect.map(IO.scala:863),
// cats.data.OptionT.getOrElse(OptionT.scala:99),
// org.http4s.syntax.KleisliResponseOps.$anonfun$orNotFound$1(KleisliSyntax.scala:49),
// repl.MdocSession$App.<init>(http4s-circe.md:47),
// repl.MdocSession$.app(http4s-circe.md:3),
// mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),
// scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23),
// scala.util.DynamicVariable.withValue(DynamicVariable.scala:62),
// scala.Console$.withErr(Console.scala:196),
// mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),
// scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23),
// scala.util.DynamicVariable.withValue(DynamicVariable.scala:62),
// scala.Console$.withOut(Console.scala:167),
// mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),
// mdoc.internal.markdown.MarkdownBuilder$.buildDocument(MarkdownBuilder.scala:44),
// mdoc.internal.markdown.Processor.processScalaInputs(Processor.scala:185),
// mdoc.internal.markdown.Processor.processScalaInputs(Processor.scala:152),
// mdoc.internal.markdown.Processor.processDocument(Processor.scala:52),
// mdoc.internal.markdown.Markdown$.toMarkdown(Markdown.scala:131),
// mdoc.internal.cli.MainOps.handleMarkdown(MainOps.scala:82),
// mdoc.internal.cli.MainOps.handleFile(MainOps.scala:110),
// mdoc.internal.cli.MainOps.$anonfun$generateCompleteSite$1(MainOps.scala:156),
// scala.collection.LinearSeqOptimized.foldLeft(LinearSeqOptimized.scala:126),
// scala.collection.LinearSeqOptimized.foldLeft$(LinearSeqOptimized.scala:122),
// scala.collection.immutable.List.foldLeft(List.scala:89),
// mdoc.internal.cli.MainOps.generateCompleteSite(MainOps.scala:155),
// mdoc.internal.cli.MainOps.run(MainOps.scala:177),
// mdoc.internal.cli.MainOps$.process(MainOps.scala:269),
// ...
println(responseBody.unsafeRunSync())
// {"health":"Sick","checks":[{"tag":"foo","health":"Sick"},{"tag":"bar","health":"Healthy"}]}
remoteHealthCheck
There’s a way to build a healthcheck out of a request to a remote one:
val remoteCheck = remoteHealthCheck[IO, Id](Request())
// remoteCheck: HealthCheck[IO, Id] = sup.HealthCheck$$anon$1@76bcbd72