org.http4s.Response Scala Examples

The following examples show how to use org.http4s.Response. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example.
Example 1
Source File: NatchezHttp4sModule.scala    From skunk   with MIT License 8 votes vote down vote up
// Copyright (c) 2018-2020 by Rob Norris
// This software is licensed under the MIT License (MIT).
// For more information see LICENSE or https://opensource.org/licenses/MIT

package natchez.http4s

import cats.~>
import cats.data.{ Kleisli, OptionT }
import cats.effect.Bracket
import cats.implicits._
import natchez.{ EntryPoint, Kernel, Span }
import org.http4s.HttpRoutes
import natchez.Trace
import natchez.Tags
import scala.util.control.NonFatal
import org.http4s.Response
import cats.effect.Resource
import cats.Defer
import natchez.TraceValue
import cats.Monad

object implicits {

  // Given an entry point and HTTP Routes in Kleisli[F, Span[F], ?] return routes in F. A new span
  // is created with the URI path as the name, either as a continuation of the incoming trace, if
  // any, or as a new root. This can likely be simplified, I just did what the types were saying
  // and it works so :shrug:
  private def liftT[F[_]: Bracket[?[_], Throwable]](
    entryPoint: EntryPoint[F])(
    routes:     HttpRoutes[Kleisli[F, Span[F], ?]]
  ): HttpRoutes[F] =
    Kleisli { req =>
      type G[A]  = Kleisli[F, Span[F], A]
      val lift   = λ[F ~> G](fa => Kleisli(_ => fa))
      val kernel = Kernel(req.headers.toList.map(h => (h.name.value -> h.value)).toMap)
      val spanR  = entryPoint.continueOrElseRoot(req.uri.path, kernel)
      OptionT {
        spanR.use { span =>
          val lower = λ[G ~> F](_(span))
          routes.run(req.mapK(lift)).mapK(lower).map(_.mapK(lower)).value
        }
      }
    }

  implicit class EntryPointOps[F[_]](self: EntryPoint[F]) {

    private def dummySpan(
      implicit ev: Monad[F]
    ): Span[F] =
      new Span[F] {
        val kernel: F[Kernel] = Kernel(Map.empty).pure[F]
        def put(fields: (String, TraceValue)*): F[Unit] = Monad[F].unit
        def span(name: String): Resource[F, Span[F]] = Monad[Resource[F, ?]].pure(this)
      }

    def liftT(routes: HttpRoutes[Kleisli[F, Span[F], ?]])(
      implicit ev: Bracket[F, Throwable]
    ): HttpRoutes[F] =
      implicits.liftT(self)(routes)

    
  def natchezMiddleware[F[_]: Bracket[?[_], Throwable]: Trace](routes: HttpRoutes[F]): HttpRoutes[F] =
    Kleisli { req =>

      val addRequestFields: F[Unit] =
        Trace[F].put(
          Tags.http.method(req.method.name),
          Tags.http.url(req.uri.renderString)
        )

      def addResponseFields(res: Response[F]): F[Unit] =
        Trace[F].put(
          Tags.http.status_code(res.status.code.toString)
        )

      def addErrorFields(e: Throwable): F[Unit] =
        Trace[F].put(
          Tags.error(true),
          "error.message"    -> e.getMessage,
          "error.stacktrace" -> e.getStackTrace.mkString("\n"),
        )

      OptionT {
        routes(req).onError {
          case NonFatal(e)   => OptionT.liftF(addRequestFields *> addErrorFields(e))
        } .value.flatMap {
          case Some(handler) => addRequestFields *> addResponseFields(handler).as(handler.some)
          case None          => Option.empty[Response[F]].pure[F]
        }
      }
    }

} 
Example 2
Source File: ResponseSyntax.scala    From http4s-poc-api   with MIT License 5 votes vote down vote up
package external
package library
package syntax

import cats.effect.Sync
import cats.syntax.applicativeError._
import org.http4s.Response

import scala.language.implicitConversions
import scala.reflect.ClassTag

private[syntax] trait ResponseSyntax {
  implicit def responseSyntax[F[_]](r: F[Response[F]]): ResponseOps[F] = new ResponseOps(r)
}

private[syntax] class ResponseOps[F[_]](private val r: F[Response[F]]) extends AnyVal {
  def handlingFailures[E <: Throwable: ClassTag](hf: E => F[Response[F]])(
    implicit ev: Sync[F]
  ): F[Response[F]] =
    r recoverWith { case e: E => hf(e) }
} 
Example 3
Source File: Utils.scala    From pizza-auth-3   with MIT License 5 votes vote down vote up
package moe.pizza.auth.webapp

import moe.pizza.auth.interfaces.UserDatabase
import moe.pizza.auth.models.Pilot
import moe.pizza.auth.webapp.Types.{HydratedSession, Session2, Session}
import moe.pizza.auth.webapp.Utils.Alerts.Alerts
import org.http4s.{Uri, Response, Request}
import org.http4s.dsl.{Root, _}

import scalaz.concurrent.Task

object Utils {
  object Alerts extends Enumeration {
    type Alerts = Value
    val success = Value("success")
    val info = Value("info")
    val warning = Value("warning")
    val danger = Value("danger")
  }

  implicit class PimpedSession2(s: Session2) {
    def hydrate(u: UserDatabase): HydratedSession = {
      new HydratedSession(s.alerts, s.redirect, s.uid.flatMap(u.getUser), s.signupData)
    }
  }

  implicit class PimpedHydratedSession(hs: HydratedSession) {
    def dehydrate(): Session2 = {
      new Session2(hs.alerts, hs.redirect, hs.pilot.map(_.uid), hs.signupData)
    }
  }

  implicit class PimpedRequest(r: Request) {
    def flash(level: Alerts, message: String): Option[HydratedSession] = {
      getSession.map(s =>
        s.copy(alerts = s.alerts :+ Types.Alert(level.toString, message)))
    }
    def getSession = r.attributes.get(SessionManager.HYDRATEDSESSION)
    def setSession(s: Types.HydratedSession): Unit =
      r.attributes.put(SessionManager.HYDRATEDSESSION, s)
    def sessionResponse(
        f: ((HydratedSession, Pilot) => Task[Response]),
        error: String = "You must be signed in to do that"): Task[Response] = {
      (getSession, getSession.flatMap(_.pilot)) match {
        case (Some(s), Some(p)) =>
          f(s, p)
        case _ =>
          TemporaryRedirect(Uri(path = "/"))
      }
    }
  }

  implicit class PimpedResponse(r: Response) {
    def withSession(s: HydratedSession): Response =
      r.withAttribute(SessionManager.HYDRATEDSESSION, s)
    def getSession(): Option[HydratedSession] =
      r.attributes.get(SessionManager.HYDRATEDSESSION)
    def withNoSession(): Response = r.withAttribute(SessionManager.LOGOUT, "")
  }

  implicit class PimpedTaskResponse(r: Task[Response]) {
    def attachSessionifDefined(s: Option[HydratedSession]): Task[Response] =
      r.map(res =>
        s.foldLeft(res) { (resp, sess) =>
          resp.withSession(sess)
      })
    def clearSession(): Task[Response] =
      r.map(res => res.withNoSession())
  }

  def sanitizeUserName(name: String) =
    name.toLowerCase.replace("'", "").replace(" ", "_")

} 
Example 4
Source File: AbstractHttpClient.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.clients

import cats.effect.{Sync, Timer}
import cats.implicits._
import ch.epfl.bluebrain.nexus.cli.CliError.ClientError
import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.{SerializationError, Unexpected}
import ch.epfl.bluebrain.nexus.cli.config.EnvConfig
import ch.epfl.bluebrain.nexus.cli.{logRetryErrors, ClientErrOr, Console}
import io.circe.Decoder
import org.http4s.circe.CirceEntityDecoder._
import org.http4s.client.Client
import org.http4s.{Request, Response}
import retry.CatsEffect._
import retry.RetryPolicy
import retry.syntax.all._

import scala.reflect.ClassTag
import scala.util.control.NonFatal

class AbstractHttpClient[F[_]: Timer](client: Client[F], env: EnvConfig)(implicit
    protected val F: Sync[F],
    protected val console: Console[F]
) {

  protected val retry                                = env.httpClient.retry
  protected def successCondition[A]                  = retry.condition.notRetryFromEither[A] _
  implicit protected val retryPolicy: RetryPolicy[F] = retry.retryPolicy
  implicit protected def logOnError[A]               = logRetryErrors[F, A]("interacting with an HTTP API")

  protected def executeDiscard[A](req: Request[F], returnValue: => A): F[ClientErrOr[A]] =
    execute(req, _.body.compile.drain.as(Right(returnValue)))

  protected def executeParse[A: Decoder](req: Request[F])(implicit A: ClassTag[A]): F[ClientErrOr[A]] =
    execute(
      req,
      _.attemptAs[A].value.map(
        _.leftMap(err =>
          SerializationError(err.message, s"The response payload was not of type '${A.runtimeClass.getSimpleName}'")
        )
      )
    )

  private def execute[A](req: Request[F], f: Response[F] => F[ClientErrOr[A]]): F[ClientErrOr[A]] =
    client
      .fetch(req)(ClientError.errorOr[F, A](r => f(r)))
      .recoverWith {
        case NonFatal(err) => F.delay(Left(Unexpected(Option(err.getMessage).getOrElse("").take(30))))
      }
      .retryingM(successCondition[A])
} 
Example 5
Source File: SparqlClientSpec.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.clients

import cats.effect.IO
import cats.implicits._
import ch.epfl.bluebrain.nexus.cli.{AbstractCliSpec, Console}
import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.{ClientStatusError, ServerStatusError}
import ch.epfl.bluebrain.nexus.cli.config.{AppConfig, EnvConfig}
import ch.epfl.bluebrain.nexus.cli.sse._
import ch.epfl.bluebrain.nexus.cli.utils.Http4sExtras
import izumi.distage.model.definition.ModuleDef
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.client.Client
import org.http4s.dsl.io._
import org.http4s.headers.`Content-Type`
import org.http4s.{HttpApp, Response, Status, Uri}
import org.scalatest.OptionValues

class SparqlClientSpec extends AbstractCliSpec with Http4sExtras with OptionValues {

  private val sparqlResultsJson = jsonContentOf("/templates/sparql-results.json")
  private val sparqlResults     = sparqlResultsJson.as[SparqlResults].toOption.value
  private val query             = "SELECT * {?s ?p ?o} LIMIT 10"

  override def overrides: ModuleDef =
    new ModuleDef {
      include(defaultModules)
      make[Client[IO]].from { cfg: AppConfig =>
        val token   = cfg.env.token
        val ct      = `Content-Type`(SparqlClient.`application/sparql-query`)
        val view    = cfg.env.defaultSparqlView.renderString
        val httpApp = HttpApp[IO] {
          // success
          case req @ POST -> `v1` / "views" / OrgLabelVar(`orgLabel`) / ProjectLabelVar(
                `projectLabel`
              ) / `view` / "sparql" contentType `ct` optbearer `token` =>
            req.as[String].flatMap {
              case `query` => Response[IO](Status.Ok).withEntity(sparqlResultsJson).pure[IO]
              case _       => Response[IO](Status.BadRequest).pure[IO]
            }
          // unknown view id
          case req @ POST -> `v1` / "views" / OrgLabelVar(`orgLabel`) / ProjectLabelVar(
                `projectLabel`
              ) / (_: String) / "sparql" contentType `ct` optbearer `token` =>
            req.as[String].flatMap {
              case `query` => Response[IO](Status.NotFound).withEntity(notFoundJson).pure[IO]
              case _       => Response[IO](Status.BadRequest).pure[IO]
            }
          // unknown token
          case req @ POST -> `v1` / "views" / OrgLabelVar(`orgLabel`) / ProjectLabelVar(
                `projectLabel`
              ) / `view` / "sparql" contentType `ct` optbearer (_: Option[
                BearerToken
              ]) =>
            req.as[String].flatMap {
              case `query` => Response[IO](Status.Forbidden).withEntity(authFailedJson).pure[IO]
              case _       => Response[IO](Status.BadRequest).pure[IO]
            }
          // other - internal error
          case req @ POST -> "v1" /: (_: Path) contentType `ct` optbearer `token` =>
            req.as[String].flatMap {
              case `query` => Response[IO](Status.InternalServerError).withEntity(internalErrorJson).pure[IO]
              case _       => Response[IO](Status.BadRequest).pure[IO]
            }
        }
        Client.fromHttpApp(httpApp)
      }
    }

  "A SparqlClient" should {
    "return sparql results" in { (client: Client[IO], console: Console[IO], env: EnvConfig) =>
      val cl = SparqlClient(client, env, console)
      for {
        results <- cl.query(orgLabel, projectLabel, query)
        _        = results shouldEqual Right(sparqlResults)
      } yield ()
    }
    "return not found" in { (client: Client[IO], console: Console[IO], env: EnvConfig) =>
      val cl = SparqlClient(client, env, console)
      for {
        results <- cl.query(orgLabel, projectLabel, Uri.unsafeFromString(genString()), query)
        _        = results shouldEqual Left(ClientStatusError(Status.NotFound, notFoundJson.noSpaces))
      } yield ()
    }
    "return internal error" in { (client: Client[IO], console: Console[IO], env: EnvConfig) =>
      val cl = SparqlClient(client, env, console)
      for {
        results <- cl.query(orgLabel, ProjectLabel(genString()), Uri.unsafeFromString(genString()), query)
        _        = results shouldEqual Left(ServerStatusError(Status.InternalServerError, internalErrorJson.noSpaces))
      } yield ()
    }
    "return bad token" in { (client: Client[IO], console: Console[IO], env: EnvConfig) =>
      val cl = SparqlClient(client, env.copy(token = Some(BearerToken("bad"))), console)
      for {
        results <- cl.query(orgLabel, projectLabel, query)
        _        = results shouldEqual Left(ClientStatusError(Status.Forbidden, authFailedJson.noSpaces))
      } yield ()
    }

  }
} 
Example 6
Source File: ProjectClientSpec.scala    From nexus   with Apache License 2.0 5 votes vote down vote up
package ch.epfl.bluebrain.nexus.cli.clients

import java.util.UUID

import cats.effect.IO
import cats.effect.concurrent.Ref
import cats.implicits._
import ch.epfl.bluebrain.nexus.cli.{AbstractCliSpec, Console}
import ch.epfl.bluebrain.nexus.cli.CliError.ClientError.ClientStatusError
import ch.epfl.bluebrain.nexus.cli.config.{AppConfig, EnvConfig}
import ch.epfl.bluebrain.nexus.cli.sse._
import ch.epfl.bluebrain.nexus.cli.utils.Http4sExtras
import izumi.distage.model.definition.ModuleDef
import org.http4s.circe.CirceEntityEncoder._
import org.http4s.client.Client
import org.http4s.dsl.io._
import org.http4s.{HttpApp, Response, Status}

class ProjectClientSpec extends AbstractCliSpec with Http4sExtras {

  private val projectJson = jsonContentOf("/templates/project.json", replacements)

  type Cache    = Map[(OrgUuid, ProjectUuid), (OrgLabel, ProjectLabel)]
  type CacheRef = Ref[IO, Cache]

  override def overrides: ModuleDef =
    new ModuleDef {
      include(defaultModules)
      make[Client[IO]].from { cfg: AppConfig =>
        val token   = cfg.env.token
        val httpApp = HttpApp[IO] {
          case GET -> `v1` / "projects" / OrgUuidVar(`orgUuid`) / ProjectUuidVar(`projectUuid`) optbearer `token` =>
            Response[IO](Status.Ok).withEntity(projectJson).pure[IO]
          case GET -> `v1` / "projects" / OrgUuidVar(_) / ProjectUuidVar(_) optbearer `token`                     =>
            Response[IO](Status.NotFound).withEntity(notFoundJson).pure[IO]
          case GET -> `v1` / "projects" / OrgUuidVar(_) / ProjectUuidVar(_) bearer (_: BearerToken)               =>
            Response[IO](Status.Forbidden).withEntity(authFailedJson).pure[IO]
        }
        Client.fromHttpApp(httpApp)
      }
      make[CacheRef].fromEffect {
        Ref.of[IO, Cache](Map.empty)
      }
    }

  "A ProjectClient" should {
    "resolve a known (orgUuid, projUuid) pair" in {
      (client: Client[IO], console: Console[IO], cache: CacheRef, env: EnvConfig) =>
        val cl = ProjectClient[IO](client, env, cache, console)
        for {
          labels <- cl.labels(orgUuid, projectUuid)
          _       = labels shouldEqual Right((orgLabel, projectLabel))
        } yield ()
    }
    "resolve from cache a known (orgUuid, projUuid) pair" in {
      (client: Client[IO], console: Console[IO], cache: CacheRef, env: EnvConfig) =>
        val errClient = Client.fromHttpApp(HttpApp[IO] { case GET -> Root => IO.pure(Response[IO](Status.NotFound)) })
        for {
          _      <- ProjectClient[IO](client, env, cache, console).labels(orgUuid, projectUuid)
          labels <- ProjectClient[IO](errClient, env, cache, console).labels(orgUuid, projectUuid)
          _       = labels shouldEqual Right((orgLabel, projectLabel))
        } yield ()
    }
    "fail to resolve an unknown (orgUuid, projUuid) pair" in {
      (client: Client[IO], console: Console[IO], cache: CacheRef, env: EnvConfig) =>
        val cl = ProjectClient[IO](client, env, cache, console)
        for {
          labels <- cl.labels(OrgUuid(UUID.randomUUID()), projectUuid)
          _       = labels shouldEqual Left(ClientStatusError(Status.NotFound, notFoundJson.noSpaces))
        } yield ()
    }
    "fail to resolve a known (orgUuid, projUuid) pair with bad credentials" in {
      (client: Client[IO], console: Console[IO], cache: CacheRef, env: EnvConfig) =>
        val cl = ProjectClient[IO](client, env.copy(token = Some(BearerToken("bad"))), cache, console)
        for {
          labels <- cl.labels(orgUuid, projectUuid)
          _       = labels shouldEqual Left(ClientStatusError(Status.Forbidden, authFailedJson.noSpaces))
        } yield ()
    }
  }
} 
Example 7
Source File: Auth.scala    From scala-pet-store   with Apache License 2.0 5 votes vote down vote up
package io.github.pauljamescleary.petstore.domain.authentication

import cats.MonadError
import cats.effect._
import io.github.pauljamescleary.petstore.domain.users.{Role, User}
import org.http4s.Response
import tsec.authentication.{
  AugmentedJWT,
  BackingStore,
  IdentityStore,
  JWTAuthenticator,
  SecuredRequest,
  TSecAuthService,
}
import tsec.authorization.BasicRBAC
import tsec.common.SecureRandomId
import tsec.jws.mac.JWSMacCV
import tsec.jwt.algorithms.JWTMacAlgo
import tsec.mac.jca.MacSigningKey

import scala.concurrent.duration._

object Auth {
  def jwtAuthenticator[F[_]: Sync, Auth: JWTMacAlgo](
      key: MacSigningKey[Auth],
      authRepo: BackingStore[F, SecureRandomId, AugmentedJWT[Auth, Long]],
      userRepo: IdentityStore[F, Long, User],
  )(implicit cv: JWSMacCV[F, Auth]): JWTAuthenticator[F, Long, User, Auth] =
    JWTAuthenticator.backed.inBearerToken(
      expiryDuration = 1.hour,
      maxIdle = None,
      tokenStore = authRepo,
      identityStore = userRepo,
      signingKey = key,
    )

  private def _allRoles[F[_], Auth](
      implicit F: MonadError[F, Throwable],
  ): BasicRBAC[F, Role, User, Auth] =
    BasicRBAC.all[F, Role, User, Auth]

  def allRoles[F[_], Auth](
      pf: PartialFunction[SecuredRequest[F, User, AugmentedJWT[Auth, Long]], F[Response[F]]],
  )(implicit F: MonadError[F, Throwable]): TSecAuthService[User, AugmentedJWT[Auth, Long], F] =
    TSecAuthService.withAuthorization(_allRoles[F, AugmentedJWT[Auth, Long]])(pf)

  def allRolesHandler[F[_], Auth](
      pf: PartialFunction[SecuredRequest[F, User, AugmentedJWT[Auth, Long]], F[Response[F]]],
  )(
      onNotAuthorized: TSecAuthService[User, AugmentedJWT[Auth, Long], F],
  )(implicit F: MonadError[F, Throwable]): TSecAuthService[User, AugmentedJWT[Auth, Long], F] =
    TSecAuthService.withAuthorizationHandler(_allRoles[F, AugmentedJWT[Auth, Long]])(
      pf,
      onNotAuthorized.run,
    )

  private def _adminOnly[F[_], Auth](
      implicit F: MonadError[F, Throwable],
  ): BasicRBAC[F, Role, User, Auth] =
    BasicRBAC[F, Role, User, Auth](Role.Admin)

  def adminOnly[F[_], Auth](
      pf: PartialFunction[SecuredRequest[F, User, AugmentedJWT[Auth, Long]], F[Response[F]]],
  )(implicit F: MonadError[F, Throwable]): TSecAuthService[User, AugmentedJWT[Auth, Long], F] =
    TSecAuthService.withAuthorization(_adminOnly[F, AugmentedJWT[Auth, Long]])(pf)
} 
Example 8
Source File: LoginTest.scala    From scala-pet-store   with Apache License 2.0 5 votes vote down vote up
package io.github.pauljamescleary.petstore
package infrastructure.endpoint

import cats.data.Kleisli
import cats.effect.IO
import domain.authentication.{LoginRequest, SignupRequest}
import domain.users.{Role, User}
import org.http4s.circe.{jsonEncoderOf, jsonOf}
import org.http4s.client.dsl.Http4sClientDsl
import org.http4s.{EntityDecoder, EntityEncoder, HttpApp, Request, Response}
import org.http4s.implicits._
import org.http4s.headers.Authorization
import io.circe.generic.auto._
import org.http4s.dsl.Http4sDsl

trait LoginTest extends Http4sClientDsl[IO] with Http4sDsl[IO] {
  implicit val userEnc: EntityEncoder[IO, User] = jsonEncoderOf
  implicit val userDec: EntityDecoder[IO, User] = jsonOf

  implicit val signUpRequestEnc: EntityEncoder[IO, SignupRequest] = jsonEncoderOf
  implicit val signUpRequestDec: EntityDecoder[IO, SignupRequest] = jsonOf

  implicit val loginRequestEnc: EntityEncoder[IO, LoginRequest] = jsonEncoderOf
  implicit val loginRequestDec: EntityDecoder[IO, LoginRequest] = jsonOf

  def signUpAndLogIn(
      userSignUp: SignupRequest,
      userEndpoint: HttpApp[IO],
  ): IO[(User, Option[Authorization])] =
    for {
      signUpRq <- POST(userSignUp, uri"/users")
      signUpResp <- userEndpoint.run(signUpRq)
      user <- signUpResp.as[User]
      loginBody = LoginRequest(userSignUp.userName, userSignUp.password)
      loginRq <- POST(loginBody, uri"/users/login")
      loginResp <- userEndpoint.run(loginRq)
    } yield {
      user -> loginResp.headers.get(Authorization)
    }

  def signUpAndLogInAsAdmin(
      userSignUp: SignupRequest,
      userEndpoint: Kleisli[IO, Request[IO], Response[IO]],
  ): IO[(User, Option[Authorization])] =
    signUpAndLogIn(userSignUp.copy(role = Role.Admin), userEndpoint)

  def signUpAndLogInAsCustomer(
      userSignUp: SignupRequest,
      userEndpoint: Kleisli[IO, Request[IO], Response[IO]],
  ): IO[(User, Option[Authorization])] =
    signUpAndLogIn(userSignUp.copy(role = Role.Customer), userEndpoint)
} 
Example 9
Source File: PriceRoutes.scala    From http4s-poc-api   with MIT License 5 votes vote down vote up
package server

import cats.effect.Sync
import cats.syntax.applicativeError._
import cats.syntax.flatMap._
import cats.syntax.functor._
import cats.syntax.show._
import errors.PriceServiceError
import errors.PriceServiceError._
import external.library.syntax.response._
import model.DomainModel._
import org.http4s.dsl.Http4sDsl
import org.http4s.{EntityDecoder, EntityEncoder, HttpRoutes, Method, Request, Response}
import service.PriceService

sealed abstract class PriceRoutes[F[_]: Sync](
  implicit requestDecoder: EntityDecoder[F, PricesRequestPayload],
  responseEncoder: EntityEncoder[F, List[Price]]
) extends Http4sDsl[F] {
  def make(priceService: PriceService[F]): HttpRoutes[F] =
    HttpRoutes.of[F] {
      case req @ Method.POST -> Root =>
        postResponse(req, priceService) handlingFailures priceServiceErrors handleErrorWith unhandledThrowable
    }

  private[this] def postResponse(request: Request[F], priceService: PriceService[F]): F[Response[F]] =
    for {
      payload <- request.as[PricesRequestPayload]
      prices  <- priceService.prices(payload.userId, payload.productIds)
      resp    <- Ok(prices)
    } yield resp

  private[this] def priceServiceErrors: PriceServiceError => F[Response[F]] = {
    case UserErr(r)                => FailedDependency(r)
    case PreferenceErr(r)          => FailedDependency(r)
    case ProductErr(r)             => FailedDependency(r)
    case ProductPriceErr(r)        => FailedDependency(r)
    case CacheLookupError(r)       => FailedDependency(r)
    case CacheStoreError(r)        => FailedDependency(r)
    case InvalidShippingCountry(r) => BadRequest(r)
  }

  private[this] def unhandledThrowable: Throwable => F[Response[F]] = { th =>
    import external.library.instances.throwable._
    InternalServerError(th.show)
  }
}

object PriceRoutes {
  def apply[
    F[_]: Sync: EntityDecoder[*[_], PricesRequestPayload]: EntityEncoder[*[_], List[Price]]
  ]: PriceRoutes[F] =
    new PriceRoutes[F] {}
} 
Example 10
Source File: TracedHttpRoute.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer

import cats.Monad
import cats.data.{Kleisli, OptionT}
import cats.syntax.flatMap._
import cats.syntax.functor._
import dev.profunktor.tracer.Tracer.TraceId
import org.http4s.{HttpRoutes, Request, Response}

object TracedHttpRoute {
  case class TracedRequest[F[_]](traceId: TraceId, request: Request[F])

  def apply[F[_]: Monad: Tracer](
      pf: PartialFunction[TracedRequest[F], F[Response[F]]]
  ): HttpRoutes[F] =
    Kleisli[OptionT[F, ?], Request[F], Response[F]] { req =>
      OptionT {
        Tracer[F]
          .getTraceId(req)
          .map(x => TracedRequest[F](x.getOrElse(TraceId("-")), req))
          .flatMap { tr =>
            val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none))
            rs.value
          }
      }

    }

} 
Example 11
Source File: ResponseVerificationSyntax.scala    From http4s-poc-api   with MIT License 5 votes vote down vote up
package syntax

import java.nio.charset.StandardCharsets

import cats.data.Validated
import cats.instances.string._
import cats.syntax.eq._
import cats.syntax.show._
import cats.syntax.validated._
import cats.{Eq, Show}
import org.http4s.{EntityDecoder, Response, Status}
import typeclasses.RunSync
import zio.Task
import zio.interop.catz._

import scala.language.implicitConversions

private[syntax] trait ResponseVerificationSyntax {
  implicit def verifiedSyntax[A](a: A): VerifiedOps[A]                     = new VerifiedOps(a)
  implicit def verifiedOptionSyntax[A](a: Option[A]): VerifiedOptionOps[A] = new VerifiedOptionOps(a)

  implicit def responseVerificationSyntax(response: Task[Response[Task]]) =
    new IoResponseResultOps(response)
}

private[syntax] class IoResponseResultOps(private val response: Task[Response[Task]]) extends AnyVal {
  import syntax.responseVerification._

  def verify[A: EntityDecoder[Task, *]](status: Status, check: A => Verified[A])(
    implicit ev1: Eq[Status],
    ev2: Show[Status],
    run: RunSync[Task]
  ): Verified[A] =
    run
      .syncUnsafe(response)
      .fold(
        err => s"Should succeed but returned the error $err".invalidNel,
        res => res.status isSameAs status andThen { _ => verifiedResponse[A](res, check) }
      )

  def verifyResponseText(status: Status, expected: String)(
    implicit ev1: Eq[Status],
    ev2: Show[Status],
    run: RunSync[Task]
  ): Verified[String] =
    run
      .syncUnsafe(response)
      .fold(
        err => s"Should succeed but returned the error $err".invalidNel,
        res => res.status isSameAs status andThen { _ => verifiedResponseText(res, expected) }
      )

  private def verifiedResponse[A: EntityDecoder[Task, *]](res: Response[Task], check: A => Verified[A])(
    implicit run: RunSync[Task]
  ): Verified[A] =
    run
      .syncUnsafe(res.as[A])
      .fold(
        respErr => s"Response should succeed but returned the error $respErr".invalidNel,
        respRes => check(respRes)
      )

  private def verifiedResponseText[A](res: Response[Task], expected: String)(
    implicit run: RunSync[Task]
  ): Verified[String] =
    run
      .syncUnsafe(res.body.compile.toVector)
      .map(_.toArray)
      .fold(
        respErr => s"Response should succeed but returned the error $respErr".invalidNel,
        respMsg => new String(respMsg, StandardCharsets.UTF_8) isSameAs expected
      )
}

private[syntax] class VerifiedOps[A](private val a: A) extends AnyVal {
  def isNotSameAs(expected: =>A)(implicit ev1: Eq[A], ev2: Show[A]): Verified[A] =
    Validated.condNel(
      a =!= expected,
      a,
      s"Unexpected value. Expected different from ${expected.show} but was ${a.show}"
    )

  def isSameAs(expected: =>A)(implicit ev1: Eq[A], ev2: Show[A]): Verified[A] =
    Validated.condNel(a === expected, a, s"Unexpected value. Expected ${expected.show} but was ${a.show}")

  def is(p: A => Boolean, reason: =>String = "")(implicit ev: Show[A]): Verified[A] =
    Validated.condNel(p(a), a, s"Unexpected value ${a.show}: Reason $reason")
}

private[syntax] class VerifiedOptionOps[A](private val a: Option[A]) extends AnyVal {
  def isNotEmpty: Verified[Option[A]] =
    Validated.condNel(a.isDefined, a, s"Unexpected empty option value")
} 
Example 12
Source File: EndpointToHttp4sServer.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.server.http4s

import cats.data._
import cats.effect.{ContextShift, Sync}
import cats.implicits._
import org.http4s.{EntityBody, HttpRoutes, Request, Response}
import org.log4s._
import sttp.tapir.monad.MonadError
import sttp.tapir.server.internal.{DecodeInputsResult, InputValues, InputValuesResult}
import sttp.tapir.server.{DecodeFailureContext, DecodeFailureHandling, ServerDefaults, ServerEndpoint, internal}
import sttp.tapir.{DecodeResult, Endpoint, EndpointIO, EndpointInput}

class EndpointToHttp4sServer[F[_]: Sync: ContextShift](serverOptions: Http4sServerOptions[F]) {
  private val outputToResponse = new OutputToHttp4sResponse[F](serverOptions)

  def toRoutes[I, E, O](se: ServerEndpoint[I, E, O, EntityBody[F], F]): HttpRoutes[F] = {
    val service: HttpRoutes[F] = HttpRoutes[F] { req: Request[F] =>
      def decodeBody(result: DecodeInputsResult): F[DecodeInputsResult] = {
        result match {
          case values: DecodeInputsResult.Values =>
            values.bodyInput match {
              case Some(bodyInput @ EndpointIO.Body(bodyType, codec, _)) =>
                new Http4sRequestToRawBody(serverOptions).apply(req.body, bodyType, req.charset, req).map { v =>
                  codec.decode(v) match {
                    case DecodeResult.Value(bodyV)     => values.setBodyInputValue(bodyV)
                    case failure: DecodeResult.Failure => DecodeInputsResult.Failure(bodyInput, failure): DecodeInputsResult
                  }
                }

              case None => (values: DecodeInputsResult).pure[F]
            }
          case failure: DecodeInputsResult.Failure => (failure: DecodeInputsResult).pure[F]
        }
      }

      def valueToResponse(value: Any): F[Response[F]] = {
        val i = value.asInstanceOf[I]
        se.logic(new CatsMonadError)(i)
          .map {
            case Right(result) => outputToResponse(ServerDefaults.StatusCodes.success, se.endpoint.output, result)
            case Left(err)     => outputToResponse(ServerDefaults.StatusCodes.error, se.endpoint.errorOutput, err)
          }
          .flatTap { response => serverOptions.logRequestHandling.requestHandled(se.endpoint, response.status.code) }
          .onError {
            case e: Exception => serverOptions.logRequestHandling.logicException(se.endpoint, e)
          }
      }

      OptionT(decodeBody(internal.DecodeInputs(se.endpoint.input, new Http4sDecodeInputsContext[F](req))).flatMap {
        case values: DecodeInputsResult.Values =>
          InputValues(se.endpoint.input, values) match {
            case InputValuesResult.Value(params, _)        => valueToResponse(params.asAny).map(_.some)
            case InputValuesResult.Failure(input, failure) => handleDecodeFailure(se.endpoint, input, failure)
          }
        case DecodeInputsResult.Failure(input, failure) => handleDecodeFailure(se.endpoint, input, failure)
      })
    }

    service
  }

  def toRoutes[I, E, O](serverEndpoints: List[ServerEndpoint[_, _, _, EntityBody[F], F]]): HttpRoutes[F] = {
    NonEmptyList.fromList(serverEndpoints.map(se => toRoutes(se))) match {
      case Some(routes) => routes.reduceK
      case None         => HttpRoutes.empty
    }
  }

  private def handleDecodeFailure[I](
      e: Endpoint[_, _, _, _],
      input: EndpointInput[_],
      failure: DecodeResult.Failure
  ): F[Option[Response[F]]] = {
    val decodeFailureCtx = DecodeFailureContext(input, failure)
    val handling = serverOptions.decodeFailureHandler(decodeFailureCtx)
    handling match {
      case DecodeFailureHandling.NoMatch =>
        serverOptions.logRequestHandling.decodeFailureNotHandled(e, decodeFailureCtx).map(_ => None)
      case DecodeFailureHandling.RespondWithResponse(output, value) =>
        serverOptions.logRequestHandling
          .decodeFailureHandled(e, decodeFailureCtx, value)
          .map(_ => Some(outputToResponse(ServerDefaults.StatusCodes.error, output, value)))
    }
  }

  private class CatsMonadError(implicit F: cats.MonadError[F, Throwable]) extends MonadError[F] {
    override def unit[T](t: T): F[T] = F.pure(t)
    override def map[T, T2](fa: F[T])(f: T => T2): F[T2] = F.map(fa)(f)
    override def flatMap[T, T2](fa: F[T])(f: T => F[T2]): F[T2] = F.flatMap(fa)(f)
    override def error[T](t: Throwable): F[T] = F.raiseError(t)
    override protected def handleWrappedError[T](rt: F[T])(h: PartialFunction[Throwable, F[T]]): F[T] = F.recoverWith(rt)(h)
  }
}

object EndpointToHttp4sServer {
  private[http4s] val log: Logger = getLogger
} 
Example 13
Source File: Http4sServerTests.scala    From tapir   with Apache License 2.0 5 votes vote down vote up
package sttp.tapir.server.http4s

import cats.data.{Kleisli, NonEmptyList}
import cats.effect._
import cats.implicits._
import org.http4s.server.Router
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.syntax.kleisli._
import org.http4s.{EntityBody, HttpRoutes, Request, Response}
import sttp.tapir.server.tests.ServerTests
import sttp.tapir.Endpoint
import sttp.tapir._
import sttp.client._
import sttp.tapir.server.{DecodeFailureHandler, ServerDefaults, ServerEndpoint}
import sttp.tapir.tests.{Port, PortCounter}

import scala.concurrent.ExecutionContext
import scala.reflect.ClassTag

class Http4sServerTests extends ServerTests[IO, EntityBody[IO], HttpRoutes[IO]] {
  implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
  implicit val contextShift: ContextShift[IO] = IO.contextShift(ec)
  implicit val timer: Timer[IO] = IO.timer(ec)

  override def pureResult[T](t: T): IO[T] = IO.pure(t)
  override def suspendResult[T](t: => T): IO[T] = IO.apply(t)

  override def route[I, E, O](
      e: ServerEndpoint[I, E, O, EntityBody[IO], IO],
      decodeFailureHandler: Option[DecodeFailureHandler] = None
  ): HttpRoutes[IO] = {
    implicit val serverOptions: Http4sServerOptions[IO] = Http4sServerOptions
      .default[IO]
      .copy(
        decodeFailureHandler = decodeFailureHandler.getOrElse(ServerDefaults.decodeFailureHandler)
      )
    e.toRoutes
  }

  override def routeRecoverErrors[I, E <: Throwable, O](e: Endpoint[I, E, O, EntityBody[IO]], fn: I => IO[O])(implicit
      eClassTag: ClassTag[E]
  ): HttpRoutes[IO] = {
    e.toRouteRecoverErrors(fn)
  }

  override def server(routes: NonEmptyList[HttpRoutes[IO]], port: Port): Resource[IO, Unit] = {
    val service: Kleisli[IO, Request[IO], Response[IO]] = routes.reduceK.orNotFound

    BlazeServerBuilder[IO](ExecutionContext.global)
      .bindHttp(port, "localhost")
      .withHttpApp(service)
      .resource
      .void
  }

  override lazy val portCounter: PortCounter = new PortCounter(56000)

  if (testNameFilter.isEmpty) {
    test("should work with a router and routes in a context") {
      val e = endpoint.get.in("test" / "router").out(stringBody).serverLogic(_ => IO.pure("ok".asRight[Unit]))
      val routes = e.toRoutes
      val port = portCounter.next()

      BlazeServerBuilder[IO](ExecutionContext.global)
        .bindHttp(port, "localhost")
        .withHttpApp(Router("/api" -> routes).orNotFound)
        .resource
        .use { _ => basicRequest.get(uri"http://localhost:$port/api/test/router").send().map(_.body shouldBe Right("ok")) }
        .unsafeRunSync()
    }
  }
} 
Example 14
Source File: InvoicesApi.scala    From event-sourcing-kafka-streams   with MIT License 5 votes vote down vote up
package org.amitayh.invoices.web

import java.util.UUID

import cats.effect.{Concurrent, Timer}
import cats.implicits._
import fs2.Stream
import fs2.concurrent.Topic
import io.circe._
import io.circe.generic.auto._
import io.circe.syntax._
import org.amitayh.invoices.common.domain.CommandResult.{Failure, Success}
import org.amitayh.invoices.common.domain.{Command, CommandResult}
import org.amitayh.invoices.dao.InvoiceList
import org.amitayh.invoices.web.CommandDto._
import org.amitayh.invoices.web.PushEvents.CommandResultRecord
import org.http4s.circe._
import org.http4s.dsl.Http4sDsl
import org.http4s.{EntityDecoder, HttpRoutes, Response}

import scala.concurrent.duration._

class InvoicesApi[F[_]: Concurrent: Timer] extends Http4sDsl[F] {

  private val maxQueued = 16

  implicit val commandEntityDecoder: EntityDecoder[F, Command] = jsonOf[F, Command]

  def service(invoiceList: InvoiceList[F],
              producer: Kafka.Producer[F, UUID, Command],
              commandResultsTopic: Topic[F, CommandResultRecord]): HttpRoutes[F] = HttpRoutes.of[F] {
    case GET -> Root / "invoices" =>
      invoiceList.get.flatMap(invoices => Ok(invoices.asJson))

    case request @ POST -> Root / "execute" / "async" / UuidVar(invoiceId) =>
      request
        .as[Command]
        .flatMap(producer.send(invoiceId, _))
        .flatMap(metaData => Accepted(Json.fromLong(metaData.timestamp)))

    case request @ POST -> Root / "execute" / UuidVar(invoiceId) =>
      request.as[Command].flatMap { command =>
        val response = resultStream(commandResultsTopic, command.commandId) merge timeoutStream
        producer.send(invoiceId, command) *> response.head.compile.toList.map(_.head)
      }
  }

  private def resultStream(commandResultsTopic: Topic[F, CommandResultRecord],
                           commandId: UUID): Stream[F, Response[F]] =
    commandResultsTopic.subscribe(maxQueued).collectFirst {
      case Some((_, CommandResult(_, `commandId`, outcome))) => outcome
    }.flatMap {
      case Success(_, _, snapshot) => Stream.eval(Ok(snapshot.asJson))
      case Failure(cause) => Stream.eval(UnprocessableEntity(cause.message))
    }

  private def timeoutStream: Stream[F, Response[F]] =
    Stream.eval(Timer[F].sleep(5.seconds) *> RequestTimeout("timeout"))

}

object InvoicesApi {
  def apply[F[_]: Concurrent: Timer]: InvoicesApi[F] = new InvoicesApi[F]
} 
Example 15
Source File: Http4sClientCircuitBreakerModule.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.http4s.client.monix.catnap

import cats.effect.{Resource, Sync}
import cats.syntax.applicativeError._
import cats.syntax.flatMap._
import monix.catnap.CircuitBreaker
import org.http4s.Response
import org.http4s.client.Client

object Http4sClientCircuitBreakerModule {

  
  def make[F[_]: Sync](
      client: Client[F],
      circuitBreaker: CircuitBreaker[F],
      httpStatusClassifier: HttpStatusClassifier = HttpStatusClassifier.default
  ): Client[F] = {
    val F = Sync[F]

    class ServerFailure(val response: Response[F], val close: F[Unit]) extends Exception

    Client[F] { request =>
      val raisedInternal = client.run(request).allocated.flatMap {
        case tuple @ (response, _) if !httpStatusClassifier.isServerFailure(response.status) => F.pure(tuple)
        case (response, close)                                                               => F.raiseError[(Response[F], F[Unit])](new ServerFailure(response, close))
      }
      val lifted = circuitBreaker.protect(raisedInternal).recover {
        case serverFailure: ServerFailure => (serverFailure.response, serverFailure.close)
      }
      Resource(lifted)
    }
  }

} 
Example 16
Source File: RouteMetrics.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.http4s.server.micrometer

import cats.effect.Sync
import cats.effect.syntax.bracket._
import cats.syntax.flatMap._
import cats.syntax.functor._
import io.micrometer.core.instrument.{MeterRegistry, Timer}
import org.http4s.Response


  def wrap(name: String)(route: => F[Response[F]]): F[Response[F]] = {
    for {
      start <- F.delay(Timer.start(meterRegistry))
      response <- route.bracket(F.pure) { response =>
        F.delay(
          start.stop(
            meterRegistry
              .timer(s"http.$name", "status", s"${response.status.code}", "status-class", s"${response.status.code / 100}xx")
          )
        ).as(())
      }
    } yield response
  }

} 
Example 17
Source File: RouteMetricsTest.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.http4s.server.micrometer

import java.util.concurrent.TimeUnit

import cats.effect.SyncIO
import io.micrometer.core.instrument.simple.SimpleMeterRegistry
import org.http4s.Response
import org.scalatest.funsuite.AnyFunSuite

class RouteMetricsTest extends AnyFunSuite {

  test("Single route metrics") {
    val registry = new SimpleMeterRegistry()
    val target = new RouteMetrics[SyncIO](registry)

    target.wrap("test")(SyncIO.pure(Response.notFound[SyncIO])).unsafeRunSync()
    assert(registry.get("http.test").timer().count() === 1)
    assert(registry.get("http.test").timer().totalTime(TimeUnit.MILLISECONDS) > 0)
    assert(registry.get("http.test").tags("status", "404").timer().count() === 1)
  }

} 
Example 18
Source File: CorrelationIdMiddleware.scala    From scala-server-toolkit   with MIT License 5 votes vote down vote up
package com.avast.sst.http4s.server.middleware

import java.util.UUID

import cats.data.{Kleisli, OptionT}
import cats.effect.Sync
import cats.syntax.functor._
import com.avast.sst.http4s.server.middleware.CorrelationIdMiddleware.CorrelationId
import io.chrisdavenport.vault.Key
import org.http4s.util.CaseInsensitiveString
import org.http4s.{Header, HttpRoutes, Request, Response}
import org.slf4j.LoggerFactory


class CorrelationIdMiddleware[F[_]: Sync](
    correlationIdHeaderName: CaseInsensitiveString,
    attributeKey: Key[CorrelationId],
    generator: () => String
) {

  private val logger = LoggerFactory.getLogger(this.getClass)

  private val F = Sync[F]

  def wrap(routes: HttpRoutes[F]): HttpRoutes[F] =
    Kleisli[OptionT[F, *], Request[F], Response[F]] { request =>
      request.headers.get(correlationIdHeaderName) match {
        case Some(header) =>
          val requestWithAttribute = request.withAttribute(attributeKey, CorrelationId(header.value))
          routes(requestWithAttribute).map(r => r.withHeaders(r.headers.put(header)))
        case None =>
          for {
            newCorrelationId <- OptionT.liftF(F.delay(generator()))
            _ <- log(newCorrelationId)
            requestWithAttribute = request.withAttribute(attributeKey, CorrelationId(newCorrelationId))
            response <- routes(requestWithAttribute)
          } yield response.withHeaders(response.headers.put(Header(correlationIdHeaderName.value, newCorrelationId)))
      }
    }

  def retrieveCorrelationId(request: Request[F]): Option[CorrelationId] = request.attributes.lookup(attributeKey)

  private def log(newCorrelationId: String) = {
    OptionT.liftF {
      F.delay {
        if (logger.isDebugEnabled()) {
          logger.debug(s"Generated new correlation ID: $newCorrelationId")
        }
      }
    }
  }
}

object CorrelationIdMiddleware {

  final case class CorrelationId(value: String) extends AnyVal

  @SuppressWarnings(Array("scalafix:Disable.toString"))
  def default[F[_]: Sync]: F[CorrelationIdMiddleware[F]] = {
    Key.newKey[F, CorrelationId].map { attributeKey =>
      new CorrelationIdMiddleware(CaseInsensitiveString("Correlation-ID"), attributeKey, () => UUID.randomUUID().toString)
    }
  }

} 
Example 19
Source File: KamonSupport.scala    From kamon-http4s   with Apache License 2.0 5 votes vote down vote up
package kamon.http4s
package middleware.client

import cats.effect.{Effect, Resource}
import cats.implicits._
import com.typesafe.config.Config
import kamon.Kamon
import kamon.context.Context
import kamon.instrumentation.http.HttpClientInstrumentation
import org.http4s.{Request, Response}
import org.http4s.client.Client

object KamonSupport {

  private var _instrumentation = instrumentation(Kamon.config())

  private def instrumentation(kamonConfig: Config): HttpClientInstrumentation = {
    val httpClientConfig = kamonConfig.getConfig("kamon.instrumentation.http4s.client")
    HttpClientInstrumentation.from(httpClientConfig, "http4s.client")
  }

  Kamon.onReconfigure(newConfig => _instrumentation = instrumentation(newConfig))


  def apply[F[_]](underlying: Client[F])(implicit F:Effect[F]): Client[F] = Client { request =>

    for {
      ctx <- Resource.liftF(F.delay(Kamon.currentContext()))
      k   <- kamonClient(underlying)(request)(ctx)(_instrumentation)
    } yield k
  }


  private def kamonClient[F[_]](underlying: Client[F])
                               (request: Request[F])
                               (ctx: Context)
                               (instrumentation: HttpClientInstrumentation)
                               (implicit F:Effect[F]): Resource[F, Response[F]] =
    for {
      requestHandler  <- Resource.liftF(F.delay(instrumentation.createHandler(getRequestBuilder(request), ctx)))
      response        <- underlying.run(requestHandler.request).attempt
      trackedResponse <- Resource.liftF(handleResponse(response, requestHandler, instrumentation.settings))
    } yield trackedResponse

  def handleResponse[F[_]](
                       response: Either[Throwable, Response[F]],
                       requestHandler: HttpClientInstrumentation.RequestHandler[Request[F]],
                       settings: HttpClientInstrumentation.Settings
                     )(implicit F:Effect[F]): F[Response[F]] =
      response match {
        case Right(res) =>
          if(res.status.code == 404) requestHandler.span.name(settings.defaultOperationName)
          requestHandler.processResponse(getResponseBuilder(res))
          F.delay(res)
        case Left(error) =>
          requestHandler.span.fail(error).finish()
          F.raiseError(error)
      }

} 
Example 20
Source File: Server.scala    From zio-metrics   with Apache License 2.0 5 votes vote down vote up
package zio.metrics.dropwizard

import scala.util.Properties.envOrNone

import cats.data.Kleisli
import org.http4s.server.blaze._
import org.http4s.{ Request, Response }

import zio.{ RIO, ZIO }
import zio.system.System
import zio.clock.Clock
import zio.console.Console
import zio.random.Random
import zio.blocking.Blocking
import zio.interop.catz._
import io.circe.Json
import org.http4s.circe._
import org.http4s.dsl.impl.Root
import org.http4s.dsl.io._
import org.http4s.{ HttpRoutes, Response }
import zio.RIO
import zio.interop.catz._
import zio.metrics.dropwizard.typeclasses._
import zio.metrics.dropwizard.DropwizardExtractor._
import cats.instances.list._
import com.codahale.metrics.MetricRegistry

object Server {
  val port: Int = envOrNone("HTTP_PORT").fold(9090)(_.toInt)

  type HttpEnvironment = Clock with Console with System with Random with Blocking
  type HttpTask[A]     = RIO[HttpEnvironment, A]

  type KleisliApp = Kleisli[HttpTask, Request[HttpTask], Response[HttpTask]]

  //type HttpApp[R <: Registry] = R => KleisliApp

  def builder[Ctx]: KleisliApp => HttpTask[Unit] =
    (app: KleisliApp) =>
      ZIO
        .runtime[HttpEnvironment]
        .flatMap { implicit rts =>
          BlazeServerBuilder[HttpTask]
            .bindHttp(port)
            .withHttpApp(app)
            .serve
            .compile
            .drain
        }

  def serveMetrics: MetricRegistry => HttpRoutes[Server.HttpTask] =
    registry =>
      HttpRoutes.of[Server.HttpTask] {
        case GET -> Root / filter => {
          println(s"filter: $filter")
          val optFilter = if (filter == "ALL") None else Some(filter)
          RegistryPrinter
            .report[List, Json](registry, optFilter)(
              (k: String, v: Json) => Json.obj((k, v))
            )
            .map(m => Response[Server.HttpTask](Ok).withEntity(m))
        }
      }
} 
Example 21
Source File: HttpAttributes.scala    From opencensus-scala   with Apache License 2.0 5 votes vote down vote up
package io.opencensus.scala.http4s

import io.opencensus.scala.http.{RequestExtractor, ResponseExtractor}
import org.http4s.util.CaseInsensitiveString
import org.http4s.{Request, Response}

private[http4s] object HttpAttributes {

  implicit def requestExtractor[F[_]]: RequestExtractor[Request[F]] =
    new RequestExtractor[Request[F]] {
      override def method(req: Request[F]): String = req.method.name

      override def userAgent(req: Request[F]): Option[String] =
        req.headers.get(CaseInsensitiveString("User-Agent")).map(_.value)

      override def path(req: Request[F]): String = req.uri.path.toString

      override def host(req: Request[F]): String = {

        val hostHeader = req.headers
          .get(CaseInsensitiveString("Host"))
          .map(_.value)

        req.uri.authority
          .map(_.host.value)
          .getOrElse(
            hostHeader
            // Having no Host header with a relative URL is invalid according to rfc2616,
            // but http4s still allows to create such HttpRequests.
              .getOrElse("")
          )
      }
    }

  implicit def responseExtractor[F[_]]: ResponseExtractor[Response[F]] =
    (res: Response[F]) => res.status.code.toLong
} 
Example 22
Source File: TracingUtils.scala    From opencensus-scala   with Apache License 2.0 5 votes vote down vote up
package io.opencensus.scala.http4s

import cats.effect.Effect
import io.opencensus.scala.Tracing
import io.opencensus.scala.http.{
  StatusTranslator,
  HttpAttributes => BaseHttpAttributes
}
import io.opencensus.scala.http4s.HttpAttributes._
import io.opencensus.trace.Span
import org.http4s.Response

object TracingUtils {
  def recordResponse[F[_]: Effect](span: Span, tracing: Tracing)(
      response: Response[F]
  ): Response[F] = {
    BaseHttpAttributes.setAttributesForResponse(span, response)
    tracing.setStatus(span, StatusTranslator.translate(response.status.code))
    response.copy(
      body = response.body.onFinalize(Effect[F].delay(tracing.endSpan(span)))
    )
  }
} 
Example 23
Source File: TracingClient.scala    From opencensus-scala   with Apache License 2.0 5 votes vote down vote up
package io.opencensus.scala.http4s

import cats.effect.{Effect, Resource}
import cats.implicits._
import io.opencensus.scala.Tracing
import io.opencensus.scala.http.propagation.Propagation
import io.opencensus.scala.http.{HttpAttributes => BaseHttpAttributes}
import io.opencensus.scala.http4s.HttpAttributes._
import io.opencensus.scala.http4s.TracingUtils.recordResponse
import io.opencensus.scala.http4s.propagation.Http4sFormatPropagation
import io.opencensus.trace.{Span, Status}
import org.http4s.client.Client
import org.http4s.{Header, Request, Response}

abstract class TracingClient[F[_]: Effect] {

  protected val tracing: Tracing
  protected val propagation: Propagation[Header, Request[F]]

  
  def trace(client: Client[F], parentSpan: Option[Span] = None): Client[F] = {
    val tracedOpen: Request[F] => Resource[F, Response[F]] =
      req =>
        for {
          span <- Resource.liftF(startSpan(parentSpan, req))
          enrichedReq = addTraceHeaders(req, span)
          res <- client
            .run(enrichedReq)
            .onError(traceError(span).andThen(x => Resource.liftF(x)))
        } yield recordResponse(span, tracing)(res)

    Client(tracedOpen)
  }

  private def traceError(span: Span): PartialFunction[Throwable, F[Unit]] = {
    case _ => recordException(span)
  }

  private def startSpan(parentSpan: Option[Span], req: Request[F]) =
    Effect[F].delay(startAndEnrichSpan(req, parentSpan))

  private def startAndEnrichSpan(
      req: Request[F],
      parentSpan: Option[Span]
  ): Span = {
    val name = req.uri.path.toString
    val span = parentSpan.fold(tracing.startSpan(name))(span =>
      tracing.startSpanWithParent(name, span)
    )
    BaseHttpAttributes.setAttributesForRequest(span, req)
    span
  }

  private def addTraceHeaders(request: Request[F], span: Span): Request[F] =
    request.withHeaders(
      request.headers.put(propagation.headersWithTracingContext(span): _*)
    )

  private def recordException(span: Span) =
    Effect[F].delay(tracing.endSpan(span, Status.INTERNAL))
}

object TracingClient {
  def apply[F[_]: Effect]: TracingClient[F] =
    new TracingClient[F] {
      override protected val tracing: Tracing = Tracing
      override protected val propagation: Propagation[Header, Request[F]] =
        new Http4sFormatPropagation[F] {}
    }
} 
Example 24
Source File: Http4sAttributesSpec.scala    From opencensus-scala   with Apache License 2.0 5 votes vote down vote up
package io.opencensus.scala.http4s

import cats.Id
import io.opencensus.scala.http.HttpAttributesSpec
import org.http4s.{Header, Headers, Request, Response, Status, Uri}

class Http4sAttributesSpec extends HttpAttributesSpec {
  import HttpAttributes._

  "Http4s attributes extraction" should behave like httpAttributes(
    request,
    response
  )

  def request: BuildRequest => Request[Id] =
    (request: BuildRequest) =>
      Request[Id](
        uri = Uri.unsafeFromString(request.host ++ request.path),
        headers = Headers(
          List(Header("User-Agent", request.userAgent)) ++ request.hostHeader
            .map(Header("Host", _))
        )
      )

  def response: Int => Response[Id] =
    (code: Int) => Response[Id](Status(code))
} 
Example 25
Source File: http4s.scala    From sup   with Apache License 2.0 5 votes vote down vote up
package sup.modules

import cats.effect.Sync
import cats.Monad
import cats.Reducible
import org.http4s.dsl.Http4sDsl
import org.http4s.EntityEncoder
import org.http4s.HttpRoutes
import org.http4s.Response
import sup.HealthCheck
import sup.HealthResult
import cats.implicits._

object http4s {

  
  def healthCheckRoutes[F[_]: Sync, H[_]: Reducible](
    healthCheck: HealthCheck[F, H],
    path: String = "health-check"
  )(
    implicit encoder: EntityEncoder[F, HealthResult[H]]
  ): HttpRoutes[F] = {

    val dsl = new Http4sDsl[F] {}
    import dsl._

    HttpRoutes.of[F] {
      case GET -> Root / `path` =>
        healthCheckResponse(healthCheck)
    }
  }

  def healthCheckResponse[F[_]: Monad, H[_]: Reducible](
    healthCheck: HealthCheck[F, H]
  )(
    implicit encoder: EntityEncoder[F, HealthResult[H]]
  ): F[Response[F]] = {

    val dsl = new Http4sDsl[F] {}
    import dsl._

    healthCheck.check.flatMap { check =>
      if (check.value.reduce.isHealthy) Ok(check)
      else ServiceUnavailable(check)
    }
  }
} 
Example 26
Source File: JdkHttpClient.scala    From http4s-jdk-http-client   with Apache License 2.0 5 votes vote down vote up
package org.http4s.client.jdkhttpclient

import java.net.URI
import java.net.http.HttpRequest.BodyPublishers
import java.net.http.HttpResponse.BodyHandlers
import java.net.http.{HttpClient, HttpRequest, HttpResponse}
import java.nio.ByteBuffer
import java.util
import java.util.concurrent.Flow

import cats.ApplicativeError
import cats.effect._
import cats.implicits._
import fs2.concurrent.SignallingRef
import fs2.interop.reactivestreams._
import fs2.{Chunk, Stream}
import org.http4s.client.Client
import org.http4s.client.jdkhttpclient.compat.CollectionConverters._
import org.http4s.internal.fromCompletionStage
import org.http4s.util.CaseInsensitiveString
import org.http4s.{Header, Headers, HttpVersion, Request, Response, Status}
import org.reactivestreams.FlowAdapters

object JdkHttpClient {

  
  def simple[F[_]](implicit F: ConcurrentEffect[F], CS: ContextShift[F]): F[Client[F]] =
    F.delay(HttpClient.newHttpClient()).map(apply(_))

  def convertHttpVersionFromHttp4s[F[_]](
      version: HttpVersion
  )(implicit F: ApplicativeError[F, Throwable]): F[HttpClient.Version] =
    version match {
      case HttpVersion.`HTTP/1.1` => HttpClient.Version.HTTP_1_1.pure[F]
      case HttpVersion.`HTTP/2.0` => HttpClient.Version.HTTP_2.pure[F]
      case _ => F.raiseError(new IllegalArgumentException("invalid HTTP version"))
    }

  // see jdk.internal.net.http.common.Utils#DISALLOWED_HEADERS_SET
  private val restrictedHeaders =
    Set(
      "connection",
      "content-length",
      "date",
      "expect",
      "from",
      "host",
      "upgrade",
      "via",
      "warning"
    ).map(CaseInsensitiveString(_))
} 
Example 27
Source File: package.scala    From tsec   with MIT License 5 votes vote down vote up
package tsec

import cats.data.OptionT
import org.bouncycastle.util.encoders.Hex
import org.http4s.server.Middleware
import org.http4s.{Request, Response}
import tsec.common.ManagedRandom

package object csrf {

  type CSRFToken = CSRFToken.Token

  object CSRFToken extends ManagedRandom {
    type Token <: String

    def apply(s: String): CSRFToken   = s.asInstanceOf[CSRFToken]
    def subst[F[_]](value: F[String]): F[CSRFToken] = value.asInstanceOf[F[CSRFToken]]

    def generateHexBase(tokenLength: Int = 32): String = {
      val tokenBytes = new Array[Byte](tokenLength)
      nextBytes(tokenBytes)
      Hex.toHexString(tokenBytes)
    }
  }

  type CSRFMiddleware[F[_]] =
    Middleware[OptionT[F, ?], Request[F], Response[F], Request[F], Response[F]]

} 
Example 28
Source File: HmacAuthMiddleware.scala    From iotchain   with MIT License 5 votes vote down vote up
package jbok.network.http.server.middleware

import java.time.{Duration, Instant}

import cats.data.{Kleisli, OptionT}
import cats.effect.Sync
import jbok.network.http.server.authentication.HMAC
import org.http4s.headers.Authorization
import org.http4s.util.CaseInsensitiveString
import org.http4s.{AuthScheme, Credentials, HttpRoutes, Request, Response, Status}
import tsec.mac.jca.{HMACSHA256, MacSigningKey}

import scala.concurrent.duration.{FiniteDuration, _}

sealed abstract class HmacAuthError(val message: String) extends Exception(message)
object HmacAuthError {
  case object NoAuthHeader     extends HmacAuthError("Could not find an Authorization header")
  case object NoDatetimeHeader extends HmacAuthError("Could not find an X-Datetime header")
  case object BadMAC           extends HmacAuthError("Bad MAC")
  case object InvalidMacFormat extends HmacAuthError("The MAC is not a valid Base64 string")
  case object InvalidDatetime  extends HmacAuthError("The datetime is not a valid UTC datetime string")
  case object Timeout          extends HmacAuthError("The request time window is closed")
}

object HmacAuthMiddleware {
  val defaultDuration: FiniteDuration = 5.minutes

  private def verifyFromHeader[F[_]](
      req: Request[F],
      key: MacSigningKey[HMACSHA256],
      duration: FiniteDuration
  ): Either[HmacAuthError, Unit] =
    for {
      authHeader <- req.headers
        .get(Authorization)
        .flatMap { t =>
          t.credentials match {
            case Credentials.Token(scheme, token) if scheme == AuthScheme.Bearer =>
              Some(token)
            case _ => None
          }
        }
        .toRight(HmacAuthError.NoAuthHeader)
      datetimeHeader <- req.headers
        .get(CaseInsensitiveString("X-Datetime"))
        .toRight(HmacAuthError.NoDatetimeHeader)
      instant <- HMAC.http.verifyFromHeader(
        req.method.name,
        req.uri.renderString,
        datetimeHeader.value,
        authHeader,
        key
      )
      _ <- Either.cond(
        Instant.now().isBefore(instant.plus(Duration.ofNanos(duration.toNanos))),
        (),
        HmacAuthError.Timeout
      )
    } yield ()

  def apply[F[_]: Sync](key: MacSigningKey[HMACSHA256], duration: FiniteDuration = defaultDuration)(routes: HttpRoutes[F]): HttpRoutes[F] =
    Kleisli { req: Request[F] =>
      verifyFromHeader(req, key, duration) match {
        case Left(error) => OptionT.some[F](Response[F](Status.Forbidden).withEntity(error.message))
        case Right(_)    => routes(req)
      }
    }
} 
Example 29
Source File: KamonSupport.scala    From kamon-http4s   with Apache License 2.0 5 votes vote down vote up
package kamon.http4s
package middleware.server

import cats.data.{Kleisli, OptionT}
import cats.effect.{Resource, Sync}
import cats.implicits._
import kamon.Kamon
import kamon.context.Storage
import kamon.instrumentation.http.HttpServerInstrumentation.RequestHandler
import kamon.instrumentation.http.HttpServerInstrumentation
import org.http4s.{HttpRoutes, Request, Response}

object KamonSupport {

  def apply[F[_]: Sync](service: HttpRoutes[F], interface: String, port: Int): HttpRoutes[F] = {
    val httpServerConfig = Kamon.config().getConfig("kamon.instrumentation.http4s.server")
    val instrumentation = HttpServerInstrumentation.from(httpServerConfig, "http4s.server", interface, port)

    Kleisli(kamonService[F](service, instrumentation)(_))
  }


  private def kamonService[F[_]](service: HttpRoutes[F], instrumentation: HttpServerInstrumentation)
                                (request: Request[F])
                                (implicit F: Sync[F]): OptionT[F, Response[F]] = OptionT {
    getHandler(instrumentation)(request).use { handler =>
      for {
        resOrUnhandled  <- service(request).value.attempt
        respWithContext <- kamonServiceHandler(handler, resOrUnhandled, instrumentation.settings)
      } yield respWithContext
    }
  }

  private def processRequest[F[_]](requestHandler: RequestHandler)(implicit F: Sync[F]): Resource[F, RequestHandler] =
    Resource.make(F.delay(requestHandler.requestReceived()))(h => F.delay(h.responseSent()))

  private def withContext[F[_]](requestHandler: RequestHandler)(implicit F: Sync[F]): Resource[F, Storage.Scope] =
    Resource.make(F.delay(Kamon.storeContext(requestHandler.context)))( scope => F.delay(scope.close()))


  private def getHandler[F[_]](instrumentation: HttpServerInstrumentation)(request: Request[F])(implicit F: Sync[F]): Resource[F, RequestHandler] =
    for {
      handler <- Resource.liftF(F.delay(instrumentation.createHandler(buildRequestMessage(request))))
      _       <- processRequest(handler)
      _       <- withContext(handler)
    } yield handler

  private def kamonServiceHandler[F[_]](requestHandler: RequestHandler,
                                        e: Either[Throwable, Option[Response[F]]],
                                       settings: HttpServerInstrumentation.Settings)
                                       (implicit F: Sync[F]): F[Option[Response[F]]] =
    e match {
      case Left(e) =>
        F.delay {
          requestHandler.span.fail(e.getMessage)
          Some(requestHandler.buildResponse(errorResponseBuilder, requestHandler.context))
        } *> F.raiseError(e)
      case Right(None) =>
        F.delay {
          requestHandler.span.name(settings.unhandledOperationName)
          val response: Response[F] = requestHandler.buildResponse[Response[F]](
            notFoundResponseBuilder, requestHandler.context
          )
          Some(response)
        }
      case Right(Some(response)) =>
        F.delay {
          val a = requestHandler.buildResponse(getResponseBuilder(response), requestHandler.context)
          Some(a)
        }
    }

} 
Example 30
Source File: package.scala    From temperature-machine   with Apache License 2.0 5 votes vote down vote up
package bad.robot.temperature

import cats.effect.IO
import org.http4s.{Header, Response, Status}
import org.specs2.matcher.{Expectable, MatchResult, Matcher}

package object test {

  def haveStatus(status: Status) = new Matcher[Response[IO]] {
    def apply[S <: Response[IO]](e: Expectable[S]): MatchResult[S] = result(
      e.value.status == status,
      s"""Status of [${e.value.status}]
          |
          |is
          |
          |$status""".stripMargin,
      s"""Status of
          |[${e.value.status}]
          |
          |is not
          |
          |[$status]
          |
          |(${e.value.as[String]})""".stripMargin,
      e)
  }

  def containsHeader(name: String, value: String) = new Matcher[Response[IO]] {
    def apply[S <: Response[IO]](e: Expectable[S]): MatchResult[S] = result(
      e.value.headers.toList.map(_.toRaw) contains Header(name, value),
      s"""${e.value.headers}
          |
          |contains
          |
          |$name""".stripMargin,
      s"""The response headers '${e.value.headers.toList.mkString("\n")}'
          |
          |do not contain
          |
          |[$name: $value]
          |""".stripMargin,
      e)
  }
} 
Example 31
Source File: Service.scala    From Learn-Scala-Programming   with MIT License 5 votes vote down vote up
package ch14

import cats.effect.IO
import ch14.Model.{Inventory, Purchase, Restock}
import org.http4s.{HttpService, MediaType, Response}
import org.http4s.dsl.Http4sDsl
import org.http4s.circe._
import io.circe.generic.auto._
import io.circe.syntax._
import org.http4s.headers.`Content-Type`
import fs2.Stream

class Service(repo: Repository) extends Http4sDsl[IO] {

  val service = HttpService[IO] {
    case DELETE -> Root / "articles" / name =>
      repo.deleteArticle(name).flatMap { if (_) NoContent() else NotFound() }

    case POST -> Root / "articles" / name =>
      repo.createArticle(name).flatMap { if (_) NoContent() else Conflict() }

    case GET -> Root / "articles" / name => renderInventory(repo.getArticle(name))

    case req @ POST -> Root / "purchase" =>
      val changes: Stream[IO, Inventory] = for {
        purchase <- Stream.eval(req.decodeJson[Purchase])
        before <- repo.getInventory
        _ <- repo.updateStock(purchase.inventory)
        after <- repo.getInventory
      } yield diff(purchase.order.keys, before, after)
      renderInventory(changes)

    case req @ POST -> Root / "restock" =>
      val newState = for {
        purchase <- Stream.eval(req.decodeJson[Restock])
        _ <- repo.updateStock(purchase.inventory)
        inventory <- repo.getInventory
      } yield inventory
      renderInventory(newState)

    case GET -> Root / "inventory" =>
      getInventory
  }

  private def diff[A](keys: Iterable[A],
                           before: Map[A, Int],
                           after: Map[A, Int]): Map[A, Int] =
    keys.filter(before.contains).map { key =>
      key -> (before(key) - after(key))
    }.toMap

  private def getInventory: IO[Response[IO]] =
    renderInventory(repo.getInventory)

  private def renderInventory(inventory: Stream[IO, Inventory]) =
    Ok(inventory.map(_.asJson.noSpaces),
       `Content-Type`(MediaType.`application/json`))

} 
Example 32
Source File: HttpErrorHandler.scala    From codepropertygraph   with Apache License 2.0 5 votes vote down vote up
package io.shiftleft.cpgserver.route

import cats.data.{Kleisli, OptionT}
import cats.effect.IO
import org.http4s.{HttpRoutes, Request, Response}

trait HttpErrorHandler {
  def handle(routes: HttpRoutes[IO]): HttpRoutes[IO]
}

object HttpErrorHandler {

  def apply(routes: HttpRoutes[IO])(handler: PartialFunction[Throwable, IO[Response[IO]]]): HttpRoutes[IO] = {
    Kleisli { req: Request[IO] =>
      OptionT {
        routes.run(req).value.handleErrorWith { e =>
          if (handler.isDefinedAt(e)) handler(e).map(Option(_))
          else IO.raiseError(e)
        }
      }
    }
  }
} 
Example 33
Source File: Http4sSpec.scala    From codepropertygraph   with Apache License 2.0 5 votes vote down vote up
package io.shiftleft.cpgserver.route

import cats.effect.IO
import org.http4s.{EntityDecoder, Response, Status}

import io.shiftleft.cpgserver.BaseSpec

trait Http4sSpec extends BaseSpec {

  // Helpfully lifted from https://http4s.org/v0.20/testing/
  def check[A](actual: IO[Response[IO]], expectedStatus: Status, expectedBody: Option[A] = None)(
      implicit ev: EntityDecoder[IO, A]
  ): Boolean = {
    val actualResp = actual.unsafeRunSync
    val statusCheck = actualResp.status == expectedStatus
    val bodyCheck =
      expectedBody.fold[Boolean](actualResp.body.compile.toVector.unsafeRunSync.isEmpty)( // Verify Response's body is empty.
        expected => actualResp.as[A].unsafeRunSync == expected)
    statusCheck && bodyCheck
  }
} 
Example 34
Source File: ResponseGenerator.scala    From docspell   with GNU General Public License v3.0 5 votes vote down vote up
package docspell.restserver.http4s

import cats.Applicative
import cats.implicits._

import org.http4s.dsl.Http4sDsl
import org.http4s.{EntityEncoder, Header, Response}

trait ResponseGenerator[F[_]] {
  self: Http4sDsl[F] =>

  implicit final class EitherResponses[A, B](e: Either[A, B]) {
    def toResponse(headers: Header*)(implicit
        F: Applicative[F],
        w0: EntityEncoder[F, A],
        w1: EntityEncoder[F, B]
    ): F[Response[F]] =
      e.fold(
        a => UnprocessableEntity(a),
        b => Ok(b)
      ).map(_.withHeaders(headers: _*))
  }

  implicit final class OptionResponse[A](o: Option[A]) {
    def toResponse(
        headers: Header*
    )(implicit F: Applicative[F], w0: EntityEncoder[F, A]): F[Response[F]] =
      o.map(a => Ok(a)).getOrElse(NotFound()).map(_.withHeaders(headers: _*))
  }

}

object ResponseGenerator {} 
Example 35
Source File: AuthTracedHttpRoute.scala    From http4s-tracer   with Apache License 2.0 5 votes vote down vote up
package dev.profunktor.tracer.auth

import cats.Monad
import cats.data.{Kleisli, OptionT}
import cats.syntax.flatMap._
import cats.syntax.functor._
import dev.profunktor.tracer.Tracer
import dev.profunktor.tracer.Tracer.TraceId
import org.http4s.{AuthedRequest, AuthedRoutes, Response}

object AuthTracedHttpRoute {
  case class AuthTracedRequest[F[_], T](traceId: TraceId, request: AuthedRequest[F, T])

  def apply[T, F[_]: Monad: Tracer](
      pf: PartialFunction[AuthTracedRequest[F, T], F[Response[F]]]
  ): AuthedRoutes[T, F] =
    Kleisli[OptionT[F, ?], AuthedRequest[F, T], Response[F]] { req =>
      OptionT {
        Tracer[F]
          .getTraceId(req.req)
          .map(x => AuthTracedRequest[F, T](x.getOrElse(TraceId("-")), req))
          .flatMap { tr =>
            val rs: OptionT[F, Response[F]] = pf.andThen(OptionT.liftF(_)).applyOrElse(tr, Function.const(OptionT.none))
            rs.value
          }
      }
    }
}